summaryrefslogtreecommitdiff
path: root/sanei
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /sanei
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'sanei')
-rw-r--r--sanei/Makefile.am21
-rw-r--r--sanei/Makefile.in675
-rw-r--r--sanei/linux_sg3_err.h135
-rw-r--r--sanei/os2_srb.h156
-rwxr-xr-xsanei/sanei_DomainOS.c528
-rwxr-xr-xsanei/sanei_DomainOS.h76
-rw-r--r--sanei/sanei_ab306.c583
-rw-r--r--sanei/sanei_access.c232
-rw-r--r--sanei/sanei_auth.c283
-rw-r--r--sanei/sanei_codec_ascii.c345
-rw-r--r--sanei/sanei_codec_bin.c139
-rw-r--r--sanei/sanei_config.c453
-rw-r--r--sanei/sanei_config2.c154
-rw-r--r--sanei/sanei_constrain_value.c309
-rw-r--r--sanei/sanei_init_debug.c150
-rw-r--r--sanei/sanei_jpeg.c235
-rw-r--r--sanei/sanei_lm983x.c265
-rw-r--r--sanei/sanei_magic.c1941
-rw-r--r--sanei/sanei_net.c186
-rw-r--r--sanei/sanei_pa4s2.c2103
-rw-r--r--sanei/sanei_pio.c604
-rw-r--r--sanei/sanei_pp.c1462
-rw-r--r--sanei/sanei_pv8630.c223
-rw-r--r--sanei/sanei_scsi.c6187
-rw-r--r--sanei/sanei_tcp.c136
-rw-r--r--sanei/sanei_thread.c562
-rw-r--r--sanei/sanei_udp.c232
-rw-r--r--sanei/sanei_usb.c3151
-rw-r--r--sanei/sanei_wire.c696
29 files changed, 22222 insertions, 0 deletions
diff --git a/sanei/Makefile.am b/sanei/Makefile.am
new file mode 100644
index 0000000..c466e44
--- /dev/null
+++ b/sanei/Makefile.am
@@ -0,0 +1,21 @@
+## 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.
+
+AM_CPPFLAGS = -I. -I$(srcdir) -I$(top_builddir)/include \
+ -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = libsanei.la
+
+libsanei_la_SOURCES = sanei_ab306.c sanei_constrain_value.c \
+ sanei_init_debug.c sanei_net.c sanei_wire.c sanei_codec_ascii.c \
+ sanei_codec_bin.c sanei_scsi.c sanei_config.c sanei_config2.c \
+ sanei_pio.c sanei_pa4s2.c sanei_auth.c sanei_usb.c sanei_thread.c \
+ sanei_pv8630.c sanei_pp.c sanei_lm983x.c sanei_access.c sanei_tcp.c \
+ sanei_udp.c sanei_magic.c
+if HAVE_JPEG
+libsanei_la_SOURCES += sanei_jpeg.c
+endif
+EXTRA_DIST = linux_sg3_err.h os2_srb.h sanei_DomainOS.c sanei_DomainOS.h
diff --git a/sanei/Makefile.in b/sanei/Makefile.in
new file mode 100644
index 0000000..66fc549
--- /dev/null
+++ b/sanei/Makefile.in
@@ -0,0 +1,675 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_JPEG_TRUE@am__append_1 = sanei_jpeg.c
+subdir = sanei
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/byteorder.m4 \
+ $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/sane/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libsanei_la_LIBADD =
+am__libsanei_la_SOURCES_DIST = sanei_ab306.c sanei_constrain_value.c \
+ sanei_init_debug.c sanei_net.c sanei_wire.c \
+ sanei_codec_ascii.c sanei_codec_bin.c sanei_scsi.c \
+ sanei_config.c sanei_config2.c sanei_pio.c sanei_pa4s2.c \
+ sanei_auth.c sanei_usb.c sanei_thread.c sanei_pv8630.c \
+ sanei_pp.c sanei_lm983x.c sanei_access.c sanei_tcp.c \
+ sanei_udp.c sanei_magic.c sanei_jpeg.c
+@HAVE_JPEG_TRUE@am__objects_1 = sanei_jpeg.lo
+am_libsanei_la_OBJECTS = sanei_ab306.lo sanei_constrain_value.lo \
+ sanei_init_debug.lo sanei_net.lo sanei_wire.lo \
+ sanei_codec_ascii.lo sanei_codec_bin.lo sanei_scsi.lo \
+ sanei_config.lo sanei_config2.lo sanei_pio.lo sanei_pa4s2.lo \
+ sanei_auth.lo sanei_usb.lo sanei_thread.lo sanei_pv8630.lo \
+ sanei_pp.lo sanei_lm983x.lo sanei_access.lo sanei_tcp.lo \
+ sanei_udp.lo sanei_magic.lo $(am__objects_1)
+libsanei_la_OBJECTS = $(am_libsanei_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include/sane
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libsanei_la_SOURCES)
+DIST_SOURCES = $(am__libsanei_la_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AVAHI_CFLAGS = @AVAHI_CFLAGS@
+AVAHI_LIBS = @AVAHI_LIBS@
+AWK = @AWK@
+BACKENDS = @BACKENDS@
+BACKEND_CONFS_ENABLED = @BACKEND_CONFS_ENABLED@
+BACKEND_LIBS_ENABLED = @BACKEND_LIBS_ENABLED@
+BACKEND_MANS_ENABLED = @BACKEND_MANS_ENABLED@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCLEAN_FILES = @DISTCLEAN_FILES@
+DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DVIPS = @DVIPS@
+DYNAMIC_FLAG = @DYNAMIC_FLAG@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GPHOTO2_CPPFLAGS = @GPHOTO2_CPPFLAGS@
+GPHOTO2_LDFLAGS = @GPHOTO2_LDFLAGS@
+GPHOTO2_LIBS = @GPHOTO2_LIBS@
+GREP = @GREP@
+HAVE_GPHOTO2 = @HAVE_GPHOTO2@
+IEEE1284_LIBS = @IEEE1284_LIBS@
+INCLUDES = @INCLUDES@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_LOCKPATH = @INSTALL_LOCKPATH@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
+LATEX = @LATEX@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUSB_1_0_CFLAGS = @LIBUSB_1_0_CFLAGS@
+LIBUSB_1_0_LIBS = @LIBUSB_1_0_LIBS@
+LIBV4L_CFLAGS = @LIBV4L_CFLAGS@
+LIBV4L_LIBS = @LIBV4L_LIBS@
+LINKER_RPATH = @LINKER_RPATH@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCKPATH_GROUP = @LOCKPATH_GROUP@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINDEX = @MAKEINDEX@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIB = @MATH_LIB@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NUMBER_VERSION = @NUMBER_VERSION@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PRELOADABLE_BACKENDS = @PRELOADABLE_BACKENDS@
+PRELOADABLE_BACKENDS_ENABLED = @PRELOADABLE_BACKENDS_ENABLED@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RESMGR_LIBS = @RESMGR_LIBS@
+SANEI_SANEI_JPEG_LO = @SANEI_SANEI_JPEG_LO@
+SANE_CONFIG_PATH = @SANE_CONFIG_PATH@
+SCSI_LIBS = @SCSI_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNMP_CONFIG_PATH = @SNMP_CONFIG_PATH@
+SOCKET_LIBS = @SOCKET_LIBS@
+STRICT_LDFLAGS = @STRICT_LDFLAGS@
+STRIP = @STRIP@
+SYSLOG_LIBS = @SYSLOG_LIBS@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+TIFF_LIBS = @TIFF_LIBS@
+USB_LIBS = @USB_LIBS@
+VERSION = @VERSION@
+V_MAJOR = @V_MAJOR@
+V_MINOR = @V_MINOR@
+V_REV = @V_REV@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+configdir = @configdir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+effective_target = @effective_target@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+locksanedir = @locksanedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I. -I$(srcdir) -I$(top_builddir)/include \
+ -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = libsanei.la
+libsanei_la_SOURCES = sanei_ab306.c sanei_constrain_value.c \
+ sanei_init_debug.c sanei_net.c sanei_wire.c \
+ sanei_codec_ascii.c sanei_codec_bin.c sanei_scsi.c \
+ sanei_config.c sanei_config2.c sanei_pio.c sanei_pa4s2.c \
+ sanei_auth.c sanei_usb.c sanei_thread.c sanei_pv8630.c \
+ sanei_pp.c sanei_lm983x.c sanei_access.c sanei_tcp.c \
+ sanei_udp.c sanei_magic.c $(am__append_1)
+EXTRA_DIST = linux_sg3_err.h os2_srb.h sanei_DomainOS.c sanei_DomainOS.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu sanei/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu sanei/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsanei.la: $(libsanei_la_OBJECTS) $(libsanei_la_DEPENDENCIES) $(EXTRA_libsanei_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libsanei_la_OBJECTS) $(libsanei_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_ab306.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_access.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_auth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_codec_ascii.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_codec_bin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_config.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_config2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_constrain_value.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_init_debug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_jpeg.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_lm983x.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_magic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_net.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_pa4s2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_pio.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_pp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_pv8630.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_scsi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_tcp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_udp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanei_wire.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sanei/linux_sg3_err.h b/sanei/linux_sg3_err.h
new file mode 100644
index 0000000..53198c0
--- /dev/null
+++ b/sanei/linux_sg3_err.h
@@ -0,0 +1,135 @@
+/* sane - Scanner Access Now Easy.
+
+ 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, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#ifndef SG_ERR_H
+#define SG_ERR_H
+
+/* Linux sg error codes taken from Doug Gilbert's sg_utils:
+ http://www.torque.net/sg/ */
+
+/* Some of the following error/status codes are exchanged between the
+ various layers of the SCSI sub-system in Linux and should never
+ reach the user. They are placed here for completeness. What appears
+ here is copied from drivers/scsi/scsi.h which is not visible in
+ the user space. */
+
+/* The following are 'host_status' codes */
+#ifndef DID_OK
+#define DID_OK 0x00
+#endif
+#ifndef DID_NO_CONNECT
+#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
+#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
+#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
+#define DID_BAD_TARGET 0x04 /* Bad target (id?) */
+#define DID_ABORT 0x05 /* Told to abort for some other reason */
+#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */
+#define DID_ERROR 0x07 /* Internal error */
+#define DID_RESET 0x08 /* Reset by somebody */
+#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */
+#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */
+#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */
+#endif
+
+
+
+/* These defines are to isolate applictaions from kernel define changes */
+#define SG_ERR_DID_OK DID_OK
+#define SG_ERR_DID_NO_CONNECT DID_NO_CONNECT
+#define SG_ERR_DID_BUS_BUSY DID_BUS_BUSY
+#define SG_ERR_DID_TIME_OUT DID_TIME_OUT
+#define SG_ERR_DID_BAD_TARGET DID_BAD_TARGET
+#define SG_ERR_DID_ABORT DID_ABORT
+#define SG_ERR_DID_PARITY DID_PARITY
+#define SG_ERR_DID_ERROR DID_ERROR
+#define SG_ERR_DID_RESET DID_RESET
+#define SG_ERR_DID_BAD_INTR DID_BAD_INTR
+#define SG_ERR_DID_PASSTHROUGH DID_PASSTHROUGH
+#define SG_ERR_DID_SOFT_ERROR DID_SOFT_ERROR
+
+/* The following are 'driver_status' codes */
+#ifndef DRIVER_OK
+#define DRIVER_OK 0x00
+#endif
+#ifndef DRIVER_BUSY
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
+
+/* Following "suggests" are "or-ed" with one of previous 8 entries */
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+#endif
+#ifndef DRIVER_MASK
+#define DRIVER_MASK 0x0f
+#endif
+#ifndef SUGGEST_MASK
+#define SUGGEST_MASK 0xf0
+#endif
+
+/* These defines are to isolate applictaions from kernel define changes */
+#define SG_ERR_DRIVER_OK DRIVER_OK
+#define SG_ERR_DRIVER_BUSY DRIVER_BUSY
+#define SG_ERR_DRIVER_SOFT DRIVER_SOFT
+#define SG_ERR_DRIVER_MEDIA DRIVER_MEDIA
+#define SG_ERR_DRIVER_ERROR DRIVER_ERROR
+#define SG_ERR_DRIVER_INVALID DRIVER_INVALID
+#define SG_ERR_DRIVER_TIMEOUT DRIVER_TIMEOUT
+#define SG_ERR_DRIVER_HARD DRIVER_HARD
+#define SG_ERR_DRIVER_SENSE DRIVER_SENSE
+#define SG_ERR_SUGGEST_RETRY SUGGEST_RETRY
+#define SG_ERR_SUGGEST_ABORT SUGGEST_ABORT
+#define SG_ERR_SUGGEST_REMAP SUGGEST_REMAP
+#define SG_ERR_SUGGEST_DIE SUGGEST_DIE
+#define SG_ERR_SUGGEST_SENSE SUGGEST_SENSE
+#define SG_ERR_SUGGEST_IS_OK SUGGEST_IS_OK
+#define SG_ERR_DRIVER_MASK DRIVER_MASK
+#define SG_ERR_SUGGEST_MASK SUGGEST_MASK
+
+#endif
diff --git a/sanei/os2_srb.h b/sanei/os2_srb.h
new file mode 100644
index 0000000..d04cf74
--- /dev/null
+++ b/sanei/os2_srb.h
@@ -0,0 +1,156 @@
+/***************************************************************************
+ * *
+ * ASPI Router Library *
+ * *
+ * This is a sample library which shows how to send SRB's to the *
+ * ASPI Router device driver. USE AT YOUR OWN RISK!! *
+ * *
+ * Version 1.01 - June 1997 *
+ * Daniel Dorau (woodst@cs.tu-berlin.de) *
+ * *
+ * Changes since 1.00: *
+ * abort(), AbortSRB added *
+ * *
+ ***************************************************************************/
+
+#pragma pack(1)
+
+ /* SRB command */
+#define SRB_Inquiry 0x00
+#define SRB_Device 0x01
+#define SRB_Command 0x02
+#define SRB_Abort 0x03
+#define SRB_Reset 0x04
+#define SRB_Param 0x05
+
+ /* SRB status */
+#define SRB_Busy 0x00 /* SCSI request in progress */
+#define SRB_Done 0x01 /* SCSI request completed without error */
+#define SRB_Aborted 0x02 /* SCSI aborted by host */
+#define SRB_BadAbort 0x03 /* Unable to abort SCSI request */
+#define SRB_Error 0x04 /* SCSI request completed with error */
+#define SRB_BusyPost 0x10 /* SCSI request in progress with POST - Nokia */
+#define SRB_InvalidCmd 0x80 /* Invalid SCSI request */
+#define SRB_InvalidHA 0x81 /* Invalid Hhost adapter number */
+#define SRB_BadDevice 0x82 /* SCSI device not installed */
+
+ /* SRB flags */
+#define SRB_Post 0x01 /* Post vector valid */
+#define SRB_Link 0x02 /* Link vector valid */
+#define SRB_SG 0x04 /* Nokia: scatter/gather */
+ /* S/G: n * (4 bytes length, 4 bytes addr) */
+ /* No of s/g items not limited by HA spec. */
+#define SRB_NoCheck 0x00 /* determined by command, not checked */
+#define SRB_Read 0x08 /* target to host, length checked */
+#define SRB_Write 0x10 /* host to target, length checked */
+#define SRB_NoTransfer 0x18 /* no data transfer */
+#define SRB_DirMask 0x18 /* bit mask */
+
+ /* SRB host adapter status */
+#define SRB_NoError 0x00 /* No host adapter detected error */
+#define SRB_Timeout 0x11 /* Selection timeout */
+#define SRB_DataLength 0x12 /* Data over/underrun */
+#define SRB_BusFree 0x13 /* Unexpected bus free */
+#define SRB_BusSequence 0x14 /* Target bus sequence failure */
+
+ /* SRB target status field */
+#define SRB_NoStatus 0x00 /* No target status */
+#define SRB_CheckStatus 0x02 /* Check status (sense data valid) */
+#define SRB_LUN_Busy 0x08 /* Specified LUN is busy */
+#define SRB_Reserved 0x18 /* Reservation conflict */
+
+#define MaxCDBStatus 64 /* max size of CDB + status */
+
+typedef struct SRB SRB;
+struct SRB {
+ unsigned char cmd, /* 00 */
+ status, /* 01 */
+ ha_num, /* 02 */
+ flags; /* 03 */
+ unsigned long res_04_07; /* 04..07 */
+ union { /* 08 */
+
+ /* SRB_Inquiry */
+ struct {
+ unsigned char num_ha, /* 08 */
+ ha_target, /* 09 */
+ aspimgr_id[16], /* 0A..19 */
+ host_id[16], /* 1A..29 */
+ unique_id[16]; /* 2A..39 */
+ } inq;
+
+ /* SRB_Device */
+ struct {
+ unsigned char target, /* 08 */
+ lun, /* 09 */
+ devtype; /* 0A */
+ } dev;
+
+ /* SRB_Command */
+ struct {
+ unsigned char target, /* 08 */
+ lun; /* 09 */
+ unsigned long data_len; /* 0A..0D */
+ unsigned char sense_len; /* 0E */
+ void * _Seg16 data_ptr; /* 0F..12 */
+ void * _Seg16 link_ptr; /* 13..16 */
+ unsigned char cdb_len, /* 17 */
+ ha_status, /* 18 */
+ target_status; /* 19 */
+ void (* _Seg16 post) (SRB *); /* 1A..1D */
+ unsigned char res_1E_29[12]; /* 1E..29 */
+ unsigned char res_2A_3F[22]; /* 2A..3F */
+ unsigned char cdb_st[64]; /* 40..7F CDB+status */
+ unsigned char res_80_BF[64]; /* 80..BF */
+ } cmd;
+
+ /* SRB_Abort */
+ struct {
+ void * _Seg16 srb; /* 08..0B */
+ } abt;
+
+ /* SRB_Reset */
+ struct {
+ unsigned char target, /* 08 */
+ lun, /* 09 */
+ res_0A_17[14], /* 0A..17 */
+ ha_status, /* 18 */
+ target_status; /* 19 */
+ } res;
+
+ /* SRB_Param - unused by ASPI4OS2 */
+ struct {
+ unsigned char unique[16]; /* 08..17 */
+ } par;
+
+ } u;
+};
+
+
+/* SCSI sense codes */
+/* Note! This list may not be complete. I did this compilation for use with tape drives.*/
+
+#define Sense_Current 0x70; /* Current Error */
+#define Sense_Deferred 0x71; /* Deferred Error */
+#define Sense_Filemark 0x80; /* Filemark detected */
+#define Sense_EOM 0x40; /* End of medium detected */
+#define Sense_ILI 0x20; /* Incorrect length indicator */
+
+/* Sense Keys */
+
+#define SK_NoSense 0x00; /* No Sense */
+#define SK_RcvrdErr 0x01; /* Recovered Error */
+#define SK_NotReady 0x02; /* Not ready */
+#define SK_MedErr 0x03; /* Medium Error */
+#define SK_HWErr 0x04; /* Hardware Error */
+#define SK_IllReq 0x05; /* Illegal Request */
+#define SK_UnitAtt 0x06; /* Unit attention */
+#define SK_DataProt 0x07: /* Data Protect */
+#define SK_BlankChk 0x08: /* Blank Check */
+#define SK_VndSpec 0x09; /* Vendor Specific */
+#define SK_CopyAbort 0x0A; /* Copy Aborted */
+#define SK_AbtdCmd 0x0B; /* Aborted Command */
+#define SK_Equal 0x0C; /* Equal */
+#define SK_VolOvfl 0x0D; /* Volume Overflow */
+#define SK_MisComp 0x0E; /* Miscompare */
+#define SK_Reserved 0x0F; /* Reserved */
diff --git a/sanei/sanei_DomainOS.c b/sanei/sanei_DomainOS.c
new file mode 100755
index 0000000..5473151
--- /dev/null
+++ b/sanei/sanei_DomainOS.c
@@ -0,0 +1,528 @@
+/* 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file defines a server for Apollo Domain/OS systems. It does all
+of the scsi_$ calls that are needed for SANE. This is necessary because
+Domain/OS will not allow a child process to access a parent's SCSI
+device. The interface is through a common, mapped area. Mutex locks
+are used to prevent concurrency problems, and eventcounts are used to
+notify a waiting process that its request has completed.
+
+ This program is intended to support only one device at a time,
+although multiple instances of this program may run concurrently. It is
+intended that this program be forked/execd by a SANE application, and
+that it will exit when the application exits.
+
+ Upon startup, the program is invoked with the path to an object that
+needs to be mapped for communication. The parent process will have
+already initialized the 'public' eventcounts and locks, and will be
+waiting for the ResultReady eventcount to be incremented. After
+initialization, the server will increment this eventcount, and wait for
+an incoming request, which is signified by the CommandAvailable
+eventcount. This EC will be incremented after another process has
+filled in the parameter area.
+
+DBG levels:
+ 0 Error - always printed.
+ 1 Basic monitor - print entry to main functions, or errors that are
+ normally suppressed because they are reported at a higher level.
+ 2 Medium monitor - show intermediate steps in functions
+ 3 Detailed monitor - if its there, print it
+
+*/
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <apollo/base.h>
+#include <apollo/ec2.h>
+#include <apollo/error.h>
+#include <apollo/fault.h>
+#include <apollo/ms.h>
+#include <apollo/mutex.h>
+#include <apollo/pfm.h>
+#include <apollo/scsi.h>
+
+#include "../include/sane/config.h"
+
+#include "../include/sane/sanei_scsi.h"
+
+#include "../include/sane/sanei_debug.h"
+
+#include "sanei_DomainOS.h"
+
+/* Timeout period for SCSI wait, in milliseconds.
+We are using 100 seconds here. */
+#define DomainScsiTimeout 100000
+
+/* Communication Area pointer */
+struct DomainServerCommon *com;
+
+/* Handle for fault handler */
+pfm_$fh_handle_t FaultHandle;
+
+
+static struct
+ {
+ void *DomainSCSIPtr; /* Pointer to the data block for this device */
+ void *DomainSensePtr; /* Pointer to the sense area for this device */
+ u_int in_use : 1; /* is this DomainFdInfo in use? */
+ u_int fake_fd : 1; /* is this a fake file descriptor? */
+ scsi_$handle_t scsi_handle; /* SCSI handle */
+ scsi_$operation_id_t op_id; /* op_id of current request */
+ } *DomainFdInfo;
+
+/* This function is called error might have occured, but it would be one that I
+don't know how to handle, or never expect to happen. */
+static void DomainErrorCheck(status_$t status, const char *message)
+ {
+ char *subsystem, *module, *code;
+ short subsystem_length, module_length, code_length;
+
+ if (status.all)
+ {
+ DBG(0, "Unrecoverable Domain/OS Error 0x%08x: %s\n", status.all, message);
+ error_$find_text(status, &subsystem, &subsystem_length, &module, &module_length, &code, &code_length);
+ if (subsystem_length && module_length && code_length)
+ DBG(0, "%.*s (%.*s/%.*s)\n", code_length, code, subsystem_length, subsystem, module_length, module);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+
+/* This function is the fault handler for the server. Currently, it only
+handles asynchronous faults. It always returns to the faulting code, but
+it disables the handler, so that the server can be killed if the parent is
+unable to do so. */
+pfm_$fh_func_val_t FaultHandler(pfm_$fault_rec_t *FaultStatusPtr)
+ {
+ status_$t status;
+
+ DBG(1, "In fault handler, status is %08x\n", FaultStatusPtr->status.all);
+ switch (FaultStatusPtr->status.all)
+ {
+ case fault_$quit:
+ pfm_$release_fault_handler(FaultHandle, &status);
+ DomainErrorCheck(status, "Can't release fault handler");
+ return pfm_$return_to_faulting_code;
+ default:
+ DBG(0, "Unrecognized fault type %08x, exiting\n", FaultStatusPtr->status.all);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+
+static void DomainSCSIOpen(void)
+ {
+ static int num_alloced = 0;
+ int fd;
+ scsi_$handle_t scsi_handle;
+ pinteger len;
+ void *DataBasePtr;
+
+ /* Find fake fd. */
+ for (fd = 0; fd < num_alloced; ++fd)
+ if (!DomainFdInfo[fd].in_use)
+ break;
+
+ /* Acquire the device */
+ DBG(1, "DomainSCSIOpen: dev='%s', fd=%d\n", com->open_path, fd);
+ len = strlen(com->open_path);
+ scsi_$acquire((char *)com->open_path, len, &scsi_handle, &com->CommandStatus);
+ if (com->CommandStatus.all != status_$ok)
+ {
+ /* we have a failure, return an error code, and generate debug output */
+ DBG(1, "DomainSCSIOpen: acquire failed, Domain/OS status is %08x\n", com->CommandStatus.all);
+ error_$print(com->CommandStatus);
+ return;
+ }
+ else
+ {
+ /* device acquired, setup buffers and buffer pointers */
+ DBG(2, "DomainSCSIOpen: acquire OK, handle is %x\n", scsi_handle);
+ /* Create/map the data area */
+ tmpnam(com->open_path);
+ DBG(2, "DomainSCSIOpen: Data block name will be '%s'\n", com->open_path);
+ DataBasePtr = ms_$crmapl(com->open_path, strlen(com->open_path), 0, DomainMaxDataSize + DomainSenseSize, ms_$cowriters, &com->CommandStatus);
+ DomainErrorCheck(com->CommandStatus, "Creating Data Area");
+ assert((((int)DataBasePtr) & 0x3ff) == 0); /* Relies on Domain/OS mapping new objects on page boundary */
+ DBG(2, "Data Buffer block created at %p, length = 0x%lx\n", DataBasePtr, DomainMaxDataSize + DomainSenseSize);
+ /* Wire the buffer */
+ scsi_$wire(scsi_handle, (void *)DataBasePtr, DomainMaxDataSize + DomainSenseSize, &com->CommandStatus);
+ if (com->CommandStatus.all == status_$ok)
+ {
+ /* success, indicate status */
+ DBG(2, "Buffer wire was successful\n");
+ }
+ else
+ {
+ /* failure, print detail and return code */
+ DBG(1, "Buffer wire failed, Domain/OS status is %08x\n", com->CommandStatus.all);
+ error_$print(com->CommandStatus);
+ return;
+ }
+ }
+
+ if (fd >= num_alloced)
+ {
+ size_t new_size, old_size;
+
+ old_size = num_alloced * sizeof (DomainFdInfo[0]);
+ num_alloced = fd + 8;
+ new_size = num_alloced * sizeof (DomainFdInfo[0]);
+ if (DomainFdInfo)
+ DomainFdInfo = realloc (DomainFdInfo, new_size);
+ else
+ DomainFdInfo = malloc (new_size);
+ memset ((char *) DomainFdInfo + old_size, 0, new_size - old_size);
+ assert(DomainFdInfo);
+ }
+ DomainFdInfo[fd].in_use = 1;
+ DomainFdInfo[fd].scsi_handle = scsi_handle;
+ DomainFdInfo[fd].DomainSCSIPtr = DataBasePtr;
+ DomainFdInfo[fd].DomainSensePtr = ((char *)DataBasePtr) + DomainMaxDataSize;
+ com->fd = fd;
+ }
+
+
+static void DomainSCSIClose(void)
+ {
+ DomainFdInfo[com->fd].in_use = 0;
+ DBG(1, "sanei_scsi_close: fd=%d\n", com->fd);
+ /* Unwire the buffer */
+ scsi_$unwire(DomainFdInfo[com->fd].scsi_handle, DomainFdInfo[com->fd].DomainSCSIPtr, DomainMaxDataSize + DomainSenseSize, true, &com->CommandStatus);
+ DomainErrorCheck(com->CommandStatus, "Unwiring SCSI buffers");
+ /* Release the device */
+ scsi_$release(DomainFdInfo[com->fd].scsi_handle, &com->CommandStatus);
+ DomainErrorCheck(com->CommandStatus, "Releasing device");
+ /* Unmap the buffer area */
+ ms_$unmap(DomainFdInfo[com->fd].DomainSCSIPtr, DomainMaxDataSize + DomainSenseSize, &com->CommandStatus);
+ DomainErrorCheck(com->CommandStatus, "Unmapping device data area");
+ }
+
+
+/* I have never seen this called, and I'm not sure what to do with it, so I
+guarantee that it will generate a fault, and I can add support for it. */
+static void DomainSCSIFlushAll(void)
+ {
+ status_$t status;
+
+ DBG(1, "DomainSCSIFlushAll: ()\n");
+ DBG(0, "Error - unimplemented feature in module" "BACKEND_NAME");
+ assert(1==0);
+ }
+
+
+/* This function must only be called from DomainSCSIEnter. The current
+server architecture requires that the Wait immediately follow the Enter
+command. */
+static void DomainSCSIWait(void)
+ {
+ int count;
+ char *ascii_wait_status, *ascii_op_status;
+ pinteger return_count;
+ scsi_$op_status_t status_list[1];
+ scsi_$wait_index_t wait_index;
+
+ /* wait for the command completion */
+ wait_index = scsi_$wait(DomainFdInfo[com->fd].scsi_handle, DomainScsiTimeout, true, DomainFdInfo[com->fd].op_id, 1, status_list, &return_count, &com->CommandStatus);
+ DBG(2, " scsi_$wait returned status = %08x\n", com->CommandStatus.all);
+ if (com->CommandStatus.all == status_$ok)
+ {
+ switch (wait_index)
+ {
+ case scsi_device_advance: ascii_wait_status = "scsi_device_advance"; break;
+ case scsi_timeout: ascii_wait_status = "scsi_timeout"; break;
+ case scsi_async_fault: ascii_wait_status = "scsi_async_fault"; break;
+ default: ascii_wait_status = "unknown"; break;
+ }
+ DBG(2, " scsi_$wait status is %s, return_count is %d\n", ascii_wait_status, return_count);
+ if (wait_index != scsi_device_advance)
+ {
+ DBG(1, "Error - SCSI timeout, or async fault\n");
+ com->CommandStatus.all = scsi_$operation_timeout;
+ }
+ else for (count = 0; count < return_count; count++)
+ {
+ switch (status_list[count].op_status)
+ {
+ case scsi_good_status: ascii_op_status = "scsi_good_status"; break;
+ case scsi_check_condition: ascii_op_status = "scsi_check_condition"; break;
+ case scsi_condition_met: ascii_op_status = "scsi_condition_met"; break;
+ case scsi_rsv1: ascii_op_status = "scsi_rsv1"; break;
+ case scsi_busy: ascii_op_status = "scsi_busy"; break;
+ case scsi_rsv2: ascii_op_status = "scsi_rsv2"; break;
+ case scsi_rsv3: ascii_op_status = "scsi_rsv3"; break;
+ case scsi_rsv4: ascii_op_status = "scsi_rsv4"; break;
+ case scsi_intermediate_good: ascii_op_status = "scsi_intermediate_good"; break;
+ case scsi_rsv5: ascii_op_status = "scsi_rsv5"; break;
+ case scsi_intermediate_condition_met: ascii_op_status = "scsi_intermediate_condition_met"; break;
+ case scsi_rsv6: ascii_op_status = "scsi_rsv6"; break;
+ case scsi_reservation_conflict: ascii_op_status = "scsi_reservation_conflict"; break;
+ case scsi_rsv7: ascii_op_status = "scsi_rsv7"; break;
+ case scsi_rsv8: ascii_op_status = "scsi_rsv8"; break;
+ case scsi_rsv9: ascii_op_status = "scsi_rsv9"; break;
+ case scsi_undefined_status: ascii_op_status = "scsi_undefined_status"; break;
+ default: ascii_op_status = "unknown"; break;
+ }
+ DBG(2, " list[%d]: op=%x cmd_status=%08x, status=%s\n", count, status_list[count].op, status_list[count].cmd_status.all, ascii_op_status);
+ switch (status_list[count].cmd_status.all)
+ {
+ case status_$ok:
+ switch (status_list[count].op_status)
+ {
+ case scsi_good_status:
+ break;
+ case scsi_busy:
+ com->CommandStatus.all = status_$ok | 0x80000000;
+ com->SCSIStatus = scsi_busy;
+ break;
+ case scsi_check_condition:
+ {
+ static unsigned char scanner_sense_cdb[] = {3, 0, 0, 0, DomainSenseSize, 0};
+ static scsi_$cdb_t sense_cdb;
+ static linteger sense_cdb_size;
+ static scsi_$operation_id_t sense_op_id;
+ static status_$t sense_status;
+ static pinteger sense_return_count;
+ static int temp;
+
+ /* Issue the sense command (wire, issue, wait, unwire */
+ sense_cdb_size = sizeof(scanner_sense_cdb);
+ memcpy(&sense_cdb, scanner_sense_cdb, sense_cdb_size);
+ scsi_$do_command_2(DomainFdInfo[com->fd].scsi_handle, sense_cdb, sense_cdb_size, DomainFdInfo[com->fd].DomainSensePtr, DomainSenseSize, scsi_read, &sense_op_id, &sense_status);
+ DomainErrorCheck(sense_status, "Executing sense command");
+ scsi_$wait(DomainFdInfo[com->fd].scsi_handle, DomainScsiTimeout, true, sense_op_id, 1, status_list, &sense_return_count, &sense_status);
+ /* The following debug output is scanner specific */
+ DBG(2, "Sense information: Error code=%02x, ASC=%02x, ASCQ=%02x\n", ((u_char *)DomainFdInfo[com->fd].DomainSensePtr)[0], ((char *)DomainFdInfo[com->fd].DomainSensePtr)[0xc], ((char *)DomainFdInfo[com->fd].DomainSensePtr)[0xd]);
+ DBG(2, " Sense dump:\n");
+ for (temp = 0; temp < DomainSenseSize; temp++)
+ DBG(2, " %02x", ((u_char *)DomainFdInfo[com->fd].DomainSensePtr)[temp]);
+ DBG(2, "\n");
+ /* see if buffer underrun - ILI/Valid are set, and command was a read */
+ /* Warning - this might be UMAX specific */
+ if ((((char *)DomainFdInfo[com->fd].DomainSensePtr)[0] == 0xf0) && (((char *)DomainFdInfo[com->fd].DomainSensePtr)[2] & 0x20) && (com->cdb.g0.cmd == 0x28))
+ {
+ /* Warning - the following code is specific to endianness and int size */
+ /* Its also very ugly */
+ DBG(2, "Shortening destination length by %x bytes\n", *(int *)(((char *)DomainFdInfo[com->fd].DomainSensePtr)+3));
+ com->dst_size -= *(int *)(((char *)DomainFdInfo[com->fd].DomainSensePtr)+3);
+ DBG(2, "Final dest size is %x\n", com->dst_size);
+ }
+ else
+ {
+ /* Set this status so that the sense handler can be called */
+ com->CommandStatus.all = status_$ok | 0x80000000;
+ com->SCSIStatus = scsi_check_condition;
+ }
+ }
+ break;
+ default:
+ /* I fault out in this case because I want to know about this error,
+ and this guarantees that it will get attention. */
+ DBG(0, "Unrecoverable Domain/OS scsi handler error: status=%08x\n", status_list[count].op_status);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ /* Handle recognized error conditions by copying the error code over */
+ case scsi_$operation_timeout:
+ case scsi_$dma_underrun: /* received by some backend code */
+ case scsi_$hdwr_failure: /* received when both scanners were active */
+ com->CommandStatus = status_list[count].cmd_status;
+ break;
+ default:
+ DBG(0, "Unrecoverable DomainOS scsi handler error: status=%08x\n", status_list[count].cmd_status.all);
+ error_$print(status_list[count].cmd_status);
+ exit(EXIT_FAILURE);
+ }
+ }
+ /* Dump the buffer contents */
+ if (com->direction == scsi_read)
+ {
+ DBG(3, "first words of buffer are:\n");
+ for (return_count = 0; return_count < com->dst_size; return_count++)
+ DBG(3, "%02X%c", ((unsigned char *)DomainFdInfo[com->fd].DomainSCSIPtr)[return_count], (return_count % 16) == 15 ? '\n' : ' ');
+ DBG(3, "\n");
+ }
+ }
+ else
+ {
+ /* scsi_$wait failed */
+ DBG(1, "scsi_$wait failed, status is %08x\n", com->CommandStatus.all);
+ }
+ }
+
+
+static void DomainSCSIEnter(void)
+ {
+ static int count;
+
+ /* Give some debug info */
+ DBG(1, "Entering DomainSCSIEnter, fd=%d, opcode=%02X\n", com->fd, com->cdb.all[0]);
+ DBG(2, " CDB Contents: ");
+ for (count = 0; count < com->cdb_size; count++)
+ DBG(2, " %02X", com->cdb.all[count]);
+ DBG(2, "\n");
+ DBG(2, "Buffer address is 0x%08x\n", DomainFdInfo[com->fd].DomainSCSIPtr);
+ DBG(2, "Buffer size is %x\n", com->buf_size);
+ DBG(2, "Direction is %s\n", (com->direction == scsi_read) ? "READ" : "WRITE");
+ /* now queue the command */
+ scsi_$do_command_2(DomainFdInfo[com->fd].scsi_handle, com->cdb, com->cdb_size, DomainFdInfo[com->fd].DomainSCSIPtr, com->buf_size, com->direction, &DomainFdInfo[com->fd].op_id, &com->CommandStatus);
+ if (com->CommandStatus.all == status_$ok)
+ {
+ /* success, indicate status */
+ DBG(2, " scsi_$do_command_2 was successful, op_id is %x\n", DomainFdInfo[com->fd].op_id);
+
+ /* If we supported multiple outstanding requests for one device, this would be
+ a good breakpoint. We would store the op_id in a private place, and construct
+ a queue for each device. This complicates things, and SANE doesn't seem to need
+ it, so it won't be implemented. The current server architecture does the wait
+ automatically, and status for the entire operation is returned. This means that
+ the sanei_scsi_req_enter and sanei_scsi_req_wait calls don't make sense, and
+ should generate an error. */
+ DomainSCSIWait();
+ }
+ else
+ {
+ /* failure, print detail and return code */
+ DBG(1, " scsi_$do_command_2 failed, status is %08x\n", com->CommandStatus.all);
+ }
+ }
+
+
+/* This function is not currently used. */
+static void DomainSCSIReqWait(void)
+ {
+ DBG(1, "sanei_scsi_req_wait: (id=%p)\n", NULL);
+ return;
+ }
+
+
+/* Startup the server */
+static void sanei_DomainOS_init(char *path)
+ {
+ int done, index;
+ long CommandTriggerValue;
+ ec2_$ptr_t CommandAvailablePtr[1];
+ status_$t status;
+ unsigned long length_mapped;
+
+ DBG(1, "Starting Domain SANE Server, common area path = '%s'\n", path);
+ com = ms_$mapl(path, strlen(path), 0, sizeof(struct DomainServerCommon), ms_$cowriters, ms_$wr, true, &length_mapped, &status);
+ DomainErrorCheck(status, "Can't open common area");
+ if (length_mapped < sizeof(struct DomainServerCommon))
+ {
+ DBG(0, "Error - can't open common area '%s' to required length\n", path);
+ DBG(0, " Required length = %lx, returned length = %lx\n", sizeof(struct DomainServerCommon), length_mapped);
+ exit(EXIT_FAILURE);
+ }
+ /* Make the file temporary, so it will disappear when it is closed */
+ ms_$mk_temporary(com, &status);
+ DomainErrorCheck(status, "Can't make common file temporary");
+ DBG(2, "Domain Server common area mapped, length is %lx\n", length_mapped);
+ /* The communication area is open, give the initial response */
+ ec2_$advance(&com->ResultReady, &status);
+ DomainErrorCheck(status, "Can't advance ResultReady EC after startup");
+ /* Enter the command loop */
+ CommandAvailablePtr[0] = &com->CommandAvailable;
+ CommandTriggerValue = ec2_$read(com->CommandAvailable) + 1;
+ /* Inhibit asynchronous faults */
+/* pfm_$inhibit();*/
+ /* Establish the fault handler */
+ FaultHandle = pfm_$establish_fault_handler(pfm_$all_faults, 0, FaultHandler, &status);
+ DomainErrorCheck(status, "Can't establish fault handler");
+ done = 0;
+ do
+ {
+ /* Wait for the command */
+ DBG(2, "Waiting for incoming command\n");
+ do
+ {
+ index = ec2_$wait_svc(CommandAvailablePtr, &CommandTriggerValue, 1, &status);
+ }
+ while (status.all == ec2_$wait_quit);
+ DomainErrorCheck(status, "Error waiting on CommandAvailable EC");
+ assert (index == 1);
+ /* Get the trigger value for next time - this avoids a race/deadlock */
+ CommandTriggerValue = ec2_$read(com->CommandAvailable) + 1;
+ /* decode/execute the command */
+ DBG(2, "Received a command - opcode is %x\n", com->opcode);
+ switch(com->opcode)
+ {
+ case Open:
+ DomainSCSIOpen();
+ ec2_$advance(&com->CommandAccepted, &status);
+ DomainErrorCheck(status, "Can't advance CommandAccepted EC on open");
+ break;
+ case Close:
+ DomainSCSIClose();
+ ec2_$advance(&com->CommandAccepted, &status);
+ DomainErrorCheck(status, "Can't advance CommandAccepted EC on close");
+ break;
+ case Enter:
+ DomainSCSIEnter();
+ ec2_$advance(&com->CommandAccepted, &status);
+ DomainErrorCheck(status, "Can't advance CommandAccepted EC on enter");
+ break;
+ case Exit:
+ done = 1;
+ /* This lets the parent know that the command was accepted. It can be
+ used to avoid sending a signal. */
+ ec2_$advance(&com->CommandAccepted, &status);
+ DomainErrorCheck(status, "Can't advance CommandAccepted EC on exit");
+ break;
+ default:
+ DBG(1, "Invalid command %x received\n", com->opcode);
+ }
+ DBG(2, "Command processing complete\n");
+ }
+ while (!done);
+ /* This would be a good place to close all devices, but for now we'll assume
+ they have already been closed by a well-behaved program */
+ /* Unmap the common area */
+ ms_$unmap(com, sizeof(struct DomainServerCommon), &status);
+ DomainErrorCheck(status, "Error unmapping common area");
+ DBG(1, "Exiting Domain SANE Server\n");
+/* pfm_$enable();*/
+ exit(EXIT_SUCCESS);
+ }
diff --git a/sanei/sanei_DomainOS.h b/sanei/sanei_DomainOS.h
new file mode 100755
index 0000000..ff93503
--- /dev/null
+++ b/sanei/sanei_DomainOS.h
@@ -0,0 +1,76 @@
+/*
+This file defines the structure of the communication area used by the
+SANE Domain/OS server. This area must be initialized before the server
+is invoked with a path to the area.
+
+To send an open command to the server, follow these steps:
+ 1) Obtain a mutex lock on CommandLock.
+ 2) Fill in opcode and open_path variables.
+ 3) Advance the CommandAvailable EC.
+ 4) Wait for the CommandAccepted EC to advance.
+ 5) Get the CommandStatus, which has the Domain/OS completion status.
+ Get the fd value, which will be used for subsequent commands.
+ Use the open_path variable to map the data section for the device.
+ 6) Release the CommandLock mutex lock.
+
+To send a close command to the server, follow these steps:
+ 1) Obtain a mutex lock on CommandLock.
+ 2) Fill in appropriate Command Data Areas.
+ 3) Advance the CommandAvailable EC.
+ 4) Wait for the CommandAccepted EC to advance.
+ 5) Get the CommandStatus, which has the Domain/OS completion status.
+ 6) Release the CommandLock mutex lock.
+
+To send an enter command to the server, follow these steps:
+ 1) Obtain a mutex lock on CommandLock.
+ 2) Fill in the fd field.
+ 3) Advance the CommandAvailable EC.
+ 4) Wait for the CommandAccepted EC to advance.
+ 5) Get the handle for the command.
+ 5) Release the CommandLock mutex lock.
+
+*/
+
+
+#ifndef DomainSenseSize
+
+/* Amount of data in a sense request */
+#define DomainSenseSize 18
+
+/* Maximum amound of data in a transfer, per Domain/OS SCSI spec */
+#define DomainMaxDataSize (32 * 1024)
+
+/* Timeout for ec2_$wait calls, in 1/4 second intervals */
+#define DomainECWaitConstant 120
+
+typedef enum {Open, Close, Enter, Exit} DomainOSOpCode;
+
+struct DomainServerCommon
+ {
+ /* Basic communication/synchronization items */
+ ec2_$eventcount_t CommandAvailable;
+ ec2_$eventcount_t CommandAccepted;
+ ec2_$eventcount_t ResultReady;
+ ec2_$eventcount_t ResultAccepted;
+ mutex_$lock_rec_t CommandLock;
+ mutex_$lock_rec_t ResultLock;
+
+ /* Command Data Areas - locked by CommandLock */
+ DomainOSOpCode opcode;
+ int fd;
+ name_$long_pname_t open_path;
+ status_$t CommandStatus;
+ scsi_$status_t SCSIStatus;
+ unsigned long CommandHandle;
+ linteger cdb_size;
+ scsi_$cdb_t cdb;
+ scsi_$direction_t direction;
+ size_t dst_size;
+ size_t buf_size;
+
+ /* Result data areas */
+ status_$t status;
+ };
+
+#endif /*DomainSenseSize*/
+
diff --git a/sanei/sanei_ab306.c b/sanei/sanei_ab306.c
new file mode 100644
index 0000000..91d647d
--- /dev/null
+++ b/sanei/sanei_ab306.c
@@ -0,0 +1,583 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Andreas Czechanowski and David Mosberger
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the Mustek-proprietary SCSI-over-parallel-port
+ interface. */
+
+#include "../include/sane/config.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_IO_H
+# include <sys/io.h> /* use where available (glibc 2.x, for example) */
+#elif HAVE_ASM_IO_H
+# include <asm/io.h> /* ugly, but backwards compatible */
+#elif defined (__i386__) && defined (__GNUC__)
+
+static __inline__ void
+outb (u_char value, u_long port)
+{
+ __asm__ __volatile__ ("outb %0,%1" : : "a" (value), "d" ((u_short) port));
+}
+
+static __inline__ u_char
+inb (u_long port)
+{
+ u_char value;
+
+ __asm__ __volatile__ ("inb %1,%0" : "=a" (value) : "d" ((u_short)port));
+ return value;
+}
+
+#else
+# define IO_SUPPORT_MISSING
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_ab306.h"
+
+#if (defined(HAVE_IOPERM) || defined(__FreeBSD__)) && !defined(IO_SUPPORT_MISSING)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME sanei_ab306
+#include "../include/sane/sanei_debug.h"
+
+#define PORT_DEV "/dev/port"
+#define AB306_CIO 0x379 /* control i/o port */
+
+#if defined(__FreeBSD__)
+static int dev_io_fd = 0;
+#endif
+
+typedef struct port
+ {
+ u_long base; /* i/o base address */
+ int port_fd; /* >= 0 when using /dev/port */
+ u_int lstat;
+ u_int in_use : 1, /* port in use? */
+ active : 1; /* port was active at some point */
+ }
+Port;
+
+static Port port[] =
+ {
+ {0x26b, -1, 0, 0, 0},
+ {0x2ab, -1, 0, 0, 0},
+ {0x2eb, -1, 0, 0, 0},
+ {0x22b, -1, 0, 0, 0},
+ {0x32b, -1, 0, 0, 0},
+ {0x36b, -1, 0, 0, 0},
+ {0x3ab, -1, 0, 0, 0},
+ {0x3eb, -1, 0, 0, 0}
+ };
+
+static const SANE_Byte wakeup[] =
+ {
+ 0x47, 0x55, 0x54, 0x53, 0x02, 0x01, 0x80
+ };
+
+static u_char cdb_sizes[8] =
+ {
+ 6, 10, 10, 12, 12, 12, 10, 10
+ };
+#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)]
+
+static void
+ab306_outb (Port *p, u_long addr, u_char val)
+{
+
+ if (p->port_fd >= 0)
+ {
+ if ((u_long) lseek (p->port_fd, addr, SEEK_SET) != addr)
+ return;
+ if (write (p->port_fd, &val, 1) != 1)
+ return;
+ }
+ else
+ outb (val, addr);
+}
+
+static int
+ab306_inb (Port *p, u_long addr)
+{
+ u_char ch;
+
+ if (p->port_fd >= 0)
+ {
+ if ((u_long) lseek (p->port_fd, addr, SEEK_SET) != addr)
+ return -1;
+ if (read (p->port_fd, &ch, 1) != 1)
+ return -1;
+ return ch;
+ }
+ else
+ return inb (addr);
+}
+
+/* Send a single command-byte over the AB306N-interface. */
+static void
+ab306_cout (Port *p, int val)
+{
+ u_long base = p->base;
+
+ while ((ab306_inb (p, base + 1) & 0x80)); /* wait for dir flag */
+ ab306_outb (p, base, val);
+ ab306_outb (p, base + 1, 0xe0);
+ while ((ab306_inb (p, base + 1) & 0x80) == 0); /* wait for ack */
+ ab306_outb (p, base + 1, 0x60);
+}
+
+/* Read a single response-byte from the SANEI_AB306N-interface. */
+static int
+ab306_cin (Port *p)
+{
+ u_long base = p->base;
+ u_char val;
+
+ while ((ab306_inb (p, base + 1) & 0x80) == 0); /* wait for dir flag */
+ val = ab306_inb (p, base);
+ ab306_outb (p, base + 1, 0xe0); /* ack received byte */
+ while (ab306_inb (p, base + 1) & 0x80);
+ ab306_outb (p, base + 1, 0x60); /* reset ack */
+ return val;
+}
+
+static SANE_Status
+ab306_write (Port *p, const void *buf, size_t len)
+{
+ u_long base = p->base;
+ u_int i;
+ int cksum = 0;
+
+ DBG(3, "ab306_write: waiting for scanner to be ready %02x\n",
+ ab306_inb (p, base + 1));
+ while ((ab306_inb (p, base + 1) & 0x20) == 0);
+ usleep (10000);
+
+ DBG(4, "ab306_write: writing data\n");
+ for (i = 0; i < len; ++i)
+ {
+ ab306_cout (p, ((const u_char *) buf)[i]);
+ cksum += ((const u_char *) buf)[i];
+ }
+
+ DBG(4, "ab306_write: writing checksum\n");
+ ab306_cout (p, -cksum & 0xff);
+
+ DBG(3, "ab306_write: waiting for scanner to be NOT ready %02x\n",
+ ab306_inb (p, base + 1));
+ while ((ab306_inb (p, base + 1) & 0x20) != 0);
+ usleep (10000);
+
+ DBG(4, "ab306_write: reading ack\n");
+ cksum = ab306_cin (p);
+ if (cksum != 0xa5)
+ {
+ DBG(0, "ab306_write: checksum error (%02x!=a5) when sending command!\n",
+ cksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Abort a running scan by pulling C6 low for a while. */
+static void
+ab306_abort (Port *p)
+{
+ ab306_outb (p, p->base + 1, 0x20);
+ while ((ab306_inb (p, p->base + 1) & 0x80));
+ ab306_outb (p, p->base + 1, 0x60);
+}
+
+/* Open the device, <dev> must contain a valid port number (as string)
+ returns port number and I/O method in <*fdp> (not a file
+ descriptor) turns the scanner on setting C5 and C6. */
+SANE_Status
+sanei_ab306_open (const char *dev, int *fdp)
+{
+ static int first_time = 1;
+ SANE_Status status;
+ u_char byte;
+ u_long base;
+ char *end;
+ int i, j;
+
+ if (first_time)
+ {
+ first_time = 0;
+ DBG_INIT();
+ }
+
+ base = strtol (dev, &end, 0);
+ if (end == dev || *end)
+ {
+ DBG(1, "sanei_ab306_open: `%s' is not a valid port number\n", dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (i = 0; i < NELEMS(port); ++i)
+ if (port[i].base == base)
+ break;
+
+ if (i >= NELEMS(port))
+ {
+ DBG(1, "sanei_ab306_open: %lx is not a valid base address\n", base);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (port[i].in_use)
+ {
+ DBG(1, "sanei_ab306_open: port %lx is already in use\n", base);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = sanei_ab306_get_io_privilege (i);
+
+#if defined(__FreeBSD__)
+ status = sanei_ab306_get_io_privilege (i);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(1, "sanei_ab306_ioport: using inb/outb access\n");
+ for (j = 0; j < NELEMS(wakeup); ++j)
+ {
+ byte = wakeup[j];
+ if (j == NELEMS(wakeup) - 1)
+ byte |= i;
+ outb (byte, AB306_CIO);
+ }
+
+#else /* !defined(__FreeBSD__) */
+ if (ioperm (AB306_CIO, 1, 1) != 0)
+ {
+ DBG(1, "sanei_ab306_ioport: using /dev/port access\n");
+ if (port[i].port_fd < 0)
+ port[i].port_fd = open (PORT_DEV, O_RDWR);
+ if (port[i].port_fd < 0)
+ return SANE_STATUS_IO_ERROR;
+ for (j = 0; j < NELEMS(wakeup); ++j)
+ {
+ if (lseek (port[i].port_fd, AB306_CIO, SEEK_SET) != AB306_CIO)
+ return SANE_STATUS_IO_ERROR;
+ byte = wakeup[j];
+ if (j == NELEMS(wakeup) - 1)
+ byte |= i;
+ if (write (port[i].port_fd, &byte, 1) != 1)
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ {
+ DBG(1, "sanei_ab306_ioport: using inb/outb access\n");
+ for (j = 0; j < NELEMS(wakeup); ++j)
+ {
+ byte = wakeup[j];
+ if (j == NELEMS(wakeup) - 1)
+ byte |= i;
+ outb (byte, AB306_CIO);
+ }
+ status = sanei_ab306_get_io_privilege (i);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+#endif /* !defined(__FreeBSD__) */
+
+ ab306_outb (port + i, port[i].base + 1, 0x60);
+ port[i].in_use = 1;
+ port[i].active = 1;
+ *fdp = i;
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_ab306_close (int fd)
+{
+ Port *p = port + fd;
+
+ if (p->in_use)
+ {
+ if (p->port_fd >= 0)
+ {
+ close (p->port_fd);
+ p->port_fd = -1;
+ }
+ p->in_use = 0;
+ }
+}
+
+/* Get I/O permission to the configuration port and the desired
+ operating ports. */
+SANE_Status
+sanei_ab306_get_io_privilege (int fd)
+{
+ if (port[fd].port_fd < 0)
+ {
+#if defined(__FreeBSD__)
+ if (dev_io_fd == 0)
+ dev_io_fd = open ("/dev/io", O_RDONLY);
+ if (dev_io_fd < 0)
+ return SANE_STATUS_IO_ERROR;
+#else /* !defined(__FreeBSD__) */
+ if (ioperm (port[fd].base, 3, 1) != 0)
+ return SANE_STATUS_IO_ERROR;
+#endif /* !defined(__FreeBSD__) */
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Send a command via the SANEI_AB306N-interface, get response when
+ <dst_size> is > 0. */
+SANE_Status
+sanei_ab306_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ Port *p = port + fd;
+ const u_char *cp = src;
+ size_t cdb_size = CDB_SIZE(cp[0]);
+ SANE_Status status;
+ u_char byte;
+
+ /* If this is a READ_SCANNED_DATA command, reset lstat: */
+ switch (cp[0])
+ {
+ case 0x08: /* scsi READ_SCANNED_DATA command */
+ /* Initialize lstat to the current status, because we need bit 4
+ (0x10) as toggle bit for reading lines. */
+ p->lstat = 0x34;
+ break;
+
+ case 0x1b: /* scsi START_STOP command */
+ if (!cp[4])
+ {
+ /* it's a STOP */
+ ab306_abort (p);
+ return SANE_STATUS_GOOD;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ status = ab306_write (p, src, 6);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (src_size > cdb_size)
+ {
+ status = ab306_write (p, cp + cdb_size, src_size - cdb_size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ if (dst && *dst_size > 0)
+ {
+ u_int i, cksum = 0;
+
+ DBG(3, "sanei_ab306_cmd: waiting for scanner to be NOT ready %02x\n",
+ ab306_inb (p, p->base + 1));
+ while ((ab306_inb (p, p->base + 1) & 0x20) != 0);
+
+ for (i = 0; i < *dst_size; i++)
+ {
+ byte = ab306_cin (p);
+ cksum += byte;
+ ((u_char *) dst)[i] = byte;
+ }
+ cksum += ab306_cin (p); /* add in checksum */
+
+ if ((cksum & 0xff) != 0)
+ {
+ DBG(0, "sanei_ab306_cmd: checksum error (%2x!=0) when receiving "
+ "after command!\n", cksum);
+ return SANE_STATUS_IO_ERROR;
+ }
+ ab306_cout (p, 0); /* dummy byte (will be discarded) */
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Read scan-data from the AB306N-device. Read <lines> lines, of which
+ every one has <bpl> bytes. */
+SANE_Status
+sanei_ab306_rdata (int fd, int planes, SANE_Byte * buf, int lines, int bpl)
+{
+ Port *p = port + fd;
+ int lcnt, pcnt, bcnt, xmax;
+ SANE_Byte *lsave_bp;
+ int nstat;
+
+ DBG(2, "sanei_ab306_rdata: start\n");
+
+ /* lstat should be set by a call to sanei_ab306_init_toggle before ! */
+ while ((ab306_inb (p, p->base + 1) & 0x80) == 0);
+ /* the lines-loop: */
+ for (lcnt = 0; lcnt < lines; ++lcnt)
+ {
+ lsave_bp = buf;
+ /* the planes-loop: */
+ for (pcnt = 0; pcnt < planes; ++pcnt)
+ {
+ xmax = bpl / planes;
+ do
+ nstat = ab306_inb (p, p->base + 1);
+ while (((p->lstat ^ nstat) & 0x10) == 0);
+
+ if (p->port_fd >= 0)
+ {
+ /* the pixel-loop: */
+ for (bcnt = 0; bcnt < xmax; bcnt++)
+ {
+ if ((u_long) lseek (p->port_fd, p->base, SEEK_SET) != p->base)
+ return SANE_STATUS_IO_ERROR;
+ if (read (p->port_fd, buf, 1) != 1)
+ return SANE_STATUS_IO_ERROR;
+ ++buf;
+ }
+ }
+ else
+ {
+ /* the pixel-loop: */
+ for (bcnt = 0; bcnt < xmax; bcnt++)
+ {
+ *(u_char *) buf = inb (p->base);
+ ++buf;
+ }
+ }
+ p->lstat = nstat;
+ }
+ }
+ DBG(2, "sanei_ab306_rdata: done\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_ab306_exit (void)
+{
+ int i;
+
+ for (i = 0; i < NELEMS(port); ++i)
+ if (port[i].active)
+ {
+ port[i].active = 0;
+ /* power off the scanner: */
+ ab306_outb (port + i, port[i].base + 1, 0x00);
+ }
+#if defined(__FreeBSD)
+ if (dev_io_fd >0)
+ close (dev_io_fd);
+#endif /* defined(__FreeBSD__) */
+}
+
+SANE_Status
+sanei_ab306_test_ready (int fd)
+{
+ Port *p = port + fd;
+ u_char byte;
+
+ byte = ab306_inb (p, p->base + 1);
+ if (byte & 0x20)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_DEVICE_BUSY;
+}
+
+#else /* !HAVE_IOPERM */
+
+SANE_Status
+sanei_ab306_open (const char *devname, int *fdp)
+{
+ *fdp = -1;
+ return SANE_STATUS_INVAL;
+}
+
+void
+sanei_ab306_close (int fd)
+{
+}
+
+void
+sanei_ab306_exit (void)
+{
+}
+
+SANE_Status
+sanei_ab306_get_io_privilege (int fd)
+{
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_ab306_test_ready (int fd)
+{
+ return SANE_STATUS_GOOD; /* non-existent device is always ready... */
+}
+
+SANE_Status
+sanei_ab306_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t *dst_size)
+{
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_ab306_rdata (int fd, int planes, SANE_Byte *buf, int lines, int bpl)
+{
+ return SANE_STATUS_INVAL;
+}
+
+#endif /* !HAVE_IOPERM */
diff --git a/sanei/sanei_access.c b/sanei/sanei_access.c
new file mode 100644
index 0000000..b77cdd9
--- /dev/null
+++ b/sanei/sanei_access.c
@@ -0,0 +1,232 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2005 Gerhard Jaeger
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+*/
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#define BACKEND_NAME sanei_access /**< name of this module for debugging */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_access.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#if defined(_WIN32) || defined(HAVE_OS2_H)
+# define PATH_SEP '\\'
+#else
+# define PATH_SEP '/'
+#endif
+
+#define REPLACEMENT_CHAR '_'
+
+#define PID_BUFSIZE 50
+
+#define PROCESS_SELF 0
+#define PROCESS_DEAD -1
+#define PROCESS_OTHER 1
+
+
+#ifdef ENABLE_LOCKING
+/** get the status/owner of a lock file
+ *
+ * The function tries to open an existing lockfile. On success, it reads out
+ * the pid which is stored inside and tries to find out more about the status
+ * of the process with the corresponding PID.
+ *
+ * @param fn - the complete filename of the lockfile to check
+ * @return
+ * - PROCESS_SELF - the calling process is owner of the lockfile
+ * - PROCESS_DEAD - the process who created the lockfile is already dead
+ * - PROCESS_OTHER - the process who created the lockfile is still alive
+ */
+static int
+get_lock_status( char *fn )
+{
+ char pid_buf[PID_BUFSIZE];
+ int fd, status;
+ pid_t pid;
+
+ fd = open( fn, O_RDONLY );
+ if( fd < 0 ) {
+ DBG( 2, "does_process_exist: open >%s< failed: %s\n",
+ fn, strerror(errno));
+ return PROCESS_OTHER;
+ }
+ read( fd, pid_buf, (PID_BUFSIZE-1));
+ pid_buf[PID_BUFSIZE-1] = '\0';
+ close( fd );
+
+ pid_buf[24] = '\0';
+ pid = strtol( pid_buf, NULL, 10 );
+ DBG( 2, "does_process_exist: PID %i\n", pid );
+
+ status = kill( pid, 0 );
+ if( status == -1 ) {
+ if( errno == ESRCH ) {
+ DBG( 2, "does_process_exist: process %i does not exist!\n", pid );
+ return PROCESS_DEAD;
+ }
+ DBG( 1, "does_process_exist: kill failed: %s\n", strerror(errno));
+ } else {
+ DBG( 2, "does_process_exist: process %i does exist!\n", pid );
+ if( pid == getpid()){
+ DBG( 2, "does_process_exist: it's me!!!\n" );
+ return PROCESS_SELF;
+ }
+ }
+ return PROCESS_OTHER;
+}
+
+static void
+create_lock_filename( char *fn, const char *devname )
+{
+ char *p;
+
+ strcpy( fn, STRINGIFY(PATH_SANE_LOCK_DIR)"/LCK.." );
+ p = &fn[strlen(fn)];
+
+ strcat( fn, devname );
+
+ while( *p != '\0' ) {
+ if( *p == PATH_SEP )
+ *p = REPLACEMENT_CHAR;
+ p++;
+ }
+ DBG( 2, "sanei_access: lockfile name >%s<\n", fn );
+}
+#endif
+
+void
+sanei_access_init( const char *backend )
+{
+ DBG_INIT();
+
+ DBG( 2, "sanei_access_init: >%s<\n", backend);
+}
+
+SANE_Status
+sanei_access_lock( const char *devicename, SANE_Word timeout )
+{
+#ifdef ENABLE_LOCKING
+ char fn[PATH_MAX];
+ char pid_buf[PID_BUFSIZE];
+ int fd, to, i;
+#endif
+
+ DBG( 2, "sanei_access_lock: devname >%s<, timeout: %u\n",
+ devicename, timeout );
+#ifndef ENABLE_LOCKING
+ return SANE_STATUS_GOOD;
+#else
+ to = timeout;
+ if (to <= 0)
+ to = 1;
+
+ create_lock_filename( fn, devicename );
+
+ for (i = 0; i < to; i++) {
+
+ fd = open( fn, O_CREAT | O_EXCL | O_WRONLY, 0644 );
+ if (fd < 0) {
+
+ if (errno == EEXIST) {
+ switch( get_lock_status( fn )) {
+ case PROCESS_DEAD:
+ DBG( 2, "sanei_access_lock: "
+ "deleting old lock file, retrying...\n" );
+ unlink( fn );
+ continue;
+ break;
+ case PROCESS_SELF:
+ DBG( 2, "sanei_access_lock: success\n" );
+ return SANE_STATUS_GOOD;
+ break;
+ default:
+ break;
+ }
+ DBG( 2, "sanei_access_lock: lock exists, waiting...\n" );
+ sleep(1);
+ } else {
+ DBG( 1, "sanei_access_lock: open >%s< failed: %s\n",
+ fn, strerror(errno));
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ DBG( 2, "sanei_access_lock: success\n" );
+ sprintf( pid_buf, "% 11i sane\n", getpid());
+ write(fd, pid_buf, strlen(pid_buf));
+ close( fd );
+ return SANE_STATUS_GOOD;
+ }
+ }
+ DBG( 1, "sanei_access_lock: timeout!\n");
+ return SANE_STATUS_ACCESS_DENIED;
+#endif
+}
+
+SANE_Status
+sanei_access_unlock( const char *devicename )
+{
+#ifdef ENABLE_LOCKING
+ char fn[PATH_MAX];
+#endif
+ DBG( 2, "sanei_access_unlock: devname >%s<\n", devicename );
+#ifdef ENABLE_LOCKING
+ create_lock_filename( fn, devicename );
+ unlink( fn );
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+/* END sanei_access.c .......................................................*/
diff --git a/sanei/sanei_auth.c b/sanei/sanei_auth.c
new file mode 100644
index 0000000..cbba06c
--- /dev/null
+++ b/sanei/sanei_auth.c
@@ -0,0 +1,283 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000 Jochen Eisinger <jochen.eisinger@gmx.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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements an interface for user authorization using MD5 digest */
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <time.h>
+
+
+#define BACKEND_NAME sanei_auth
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_debug.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_auth.h"
+#include "../include/sane/sanei_config.h"
+
+#include "../include/md5.h"
+
+static int random_seeded = 0;
+
+#define INIT_RND() do { \
+ if (random_seeded == 0) { \
+ random_seeded = 1; \
+ srand(time(NULL)); \
+ DBG_INIT(); \
+ } \
+ } while (0)
+
+
+#ifdef HAVE_DEV_URANDOM
+
+static unsigned long
+randombits (void)
+{
+
+ FILE *dev_urandom;
+ unsigned long result = 0;
+ char buffer[4];
+
+ if ((dev_urandom = fopen ("/dev/urandom", "r")) == NULL)
+ {
+ DBG (2, "randombits: could not open /dev/urandom...\n");
+ return rand ();
+ }
+
+ fread (buffer, 1, 4, dev_urandom);
+
+ fclose (dev_urandom);
+
+ result = buffer[0];
+ result <<= 8;
+ result += buffer[1];
+ result <<= 8;
+ result += buffer[2];
+ result <<= 8;
+ result += buffer[3];
+
+ return result;
+
+}
+
+#else
+
+#define randombits rand
+
+#endif
+
+
+static int
+check_passwd (const char *upassword,
+ const char *password,
+ const char *randomstring, const char *username)
+{
+
+ unsigned char md5digest[16];
+ char tmpstr[512];
+
+ if (strncmp (upassword, "$MD5$", 5) == 0)
+ {
+
+ sprintf (tmpstr, "%s%.128s",
+ strstr (randomstring, "$MD5$") + 5, password);
+ md5_buffer (tmpstr, strlen (tmpstr), md5digest);
+
+ sprintf (tmpstr, "$MD5$%02x%02x%02x%02x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x%02x%02x%02x%02x",
+ md5digest[0], md5digest[1],
+ md5digest[2], md5digest[3],
+ md5digest[4], md5digest[5],
+ md5digest[6], md5digest[7],
+ md5digest[8], md5digest[9],
+ md5digest[10], md5digest[11],
+ md5digest[12], md5digest[13], md5digest[14], md5digest[15]);
+
+
+ return (strcmp (upassword, tmpstr) == 0);
+
+ }
+ else
+ {
+
+ DBG (1, "check_passwd: received plain-text reply from user ``%s''\n",
+ username);
+
+ return (strcmp (upassword, password) == 0);
+
+ }
+}
+
+
+SANE_Status
+sanei_authorize (const char *resource,
+ const char *backend, SANE_Auth_Callback authorize)
+{
+ FILE *passwd_file;
+ char passwd_filename[256];
+ char line[1024], *linep;
+ SANE_Bool entry_found = SANE_FALSE;
+ char md5resource[256];
+ char username[SANE_MAX_USERNAME_LEN];
+ char password[SANE_MAX_PASSWORD_LEN];
+
+ INIT_RND ();
+
+ DBG (4, "called for ``%s'' by %s\n", resource, backend);
+
+ if (strlen (resource) > 127)
+ DBG (1, "resource is longer than 127 chars...\n");
+
+ sprintf (passwd_filename, "%s.users", backend);
+
+ passwd_file = sanei_config_open (passwd_filename);
+
+ if (passwd_file == NULL)
+ {
+ DBG (3, "could not open ``%s''...\n", passwd_filename);
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (line, 1024, passwd_file))
+ {
+
+ if (strchr (line, ':') != NULL)
+ {
+ if (strchr (strchr (line, ':') + 1, ':') != NULL)
+ {
+
+ if (strcmp (strchr (strchr (line, ':') + 1, ':') + 1, resource)
+ == 0)
+
+ {
+
+
+
+ entry_found = SANE_TRUE;
+ break;
+
+ }
+ }
+
+ }
+
+ }
+
+ if (entry_found == SANE_FALSE)
+ {
+
+ fclose (passwd_file);
+
+ DBG (3, "could not find resource ``%s''...\n", resource);
+ return SANE_STATUS_GOOD;
+
+ }
+
+ if (authorize == NULL)
+ {
+ DBG (2, "no authorization callback supplied by frontend\n");
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ sprintf (md5resource, "%.128s$MD5$%x%lx%08lx",
+ resource, getpid (), (long int) time (NULL), randombits ());
+
+ DBG(0, "resource=%s\n", md5resource);
+
+ memset (username, 0, SANE_MAX_USERNAME_LEN);
+ memset (password, 0, SANE_MAX_PASSWORD_LEN);
+
+ (*authorize) (md5resource, username, password);
+
+
+ fseek (passwd_file, 0L, SEEK_SET);
+
+ while (sanei_config_read (line, 1024, passwd_file))
+ {
+
+ if ((strlen (line) > 0) && (line[strlen (line) - 1] == '\n'))
+ line[strlen (line) - 1] = '\n';
+
+ if ((strlen (line) > 0) && (line[strlen (line) - 1] == '\r'))
+ line[strlen (line) - 1] = '\r';
+
+
+ if ((strncmp (line, username, strlen (username)) == 0) &&
+ (((strchr (line, ':')) - line) == (signed) strlen (username)))
+ {
+
+ linep = strchr (line, ':') + 1;
+
+ if ((strchr (linep, ':') != NULL)
+ && (strcmp (strchr (linep, ':') + 1, resource) == 0))
+ {
+
+ *(strchr (linep, ':')) = 0;
+
+
+ if (check_passwd (password, linep, md5resource, username))
+ {
+ fclose (passwd_file);
+ DBG (2, "authorization succeeded\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+ }
+
+
+ }
+
+ fclose (passwd_file);
+
+ DBG (1, "authorization failed\n");
+
+ return SANE_STATUS_ACCESS_DENIED;
+}
diff --git a/sanei/sanei_codec_ascii.c b/sanei/sanei_codec_ascii.c
new file mode 100644
index 0000000..dc9d4af
--- /dev/null
+++ b/sanei/sanei_codec_ascii.c
@@ -0,0 +1,345 @@
+/* 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_wire.h"
+#include "../include/sane/sanei_codec_ascii.h"
+
+static const char *hexdigit = "0123456789abcdef";
+
+static void
+skip_ws (Wire *w)
+{
+ while (1)
+ {
+ sanei_w_space (w, 1);
+ if (w->status != 0)
+ return;
+
+ if (!isspace (*w->buffer.curr))
+ return;
+
+ ++w->buffer.curr;
+ }
+}
+
+static unsigned
+get_digit (Wire *w)
+{
+ unsigned digit;
+
+ sanei_w_space (w, 1);
+ digit = tolower(*w->buffer.curr++) - '0';
+ if (digit > 9)
+ digit -= 'a' - ('9' + 1);
+ if (digit > 0xf)
+ {
+ w->status = EINVAL;
+ return 0;
+ }
+ return digit;
+}
+
+static SANE_Byte
+get_byte (Wire *w)
+{
+ return get_digit (w) << 4 | get_digit (w);
+}
+
+static void
+ascii_w_byte (Wire *w, void *v)
+{
+ SANE_Byte *b = v;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ sanei_w_space (w, 3);
+ *w->buffer.curr++ = hexdigit[(*b >> 4) & 0x0f];
+ *w->buffer.curr++ = hexdigit[(*b >> 0) & 0x0f];
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ skip_ws (w);
+ *b = get_byte (w);
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+static void
+ascii_w_char (Wire *w, void *v)
+{
+ SANE_Char *c = v;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ sanei_w_space (w, 5);
+ *w->buffer.curr++ = '\'';
+ if (*c == '\'' || *c == '\\')
+ *w->buffer.curr++ = '\\';
+ *w->buffer.curr++ = *c;
+ *w->buffer.curr++ = '\'';
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ sanei_w_space (w, 4);
+ if (*w->buffer.curr++ != '\'')
+ {
+ w->status = EINVAL;
+ return;
+ }
+ *c = *w->buffer.curr++;
+ if (*c == '\\')
+ {
+ sanei_w_space (w, 2);
+ *c = *w->buffer.curr++;
+ }
+ if (*w->buffer.curr++ != '\'')
+ {
+ w->status = EINVAL;
+ return;
+ }
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+static void
+ascii_w_string (Wire *w, void *v)
+{
+ size_t len, alloced_len;
+ SANE_String *s = v;
+ char * str, ch;
+ int done;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ if (*s)
+ {
+ sanei_w_space (w, 1);
+ *w->buffer.curr++ = '"';
+ str = *s;
+ while ((ch = *str++))
+ {
+ sanei_w_space (w, 2);
+ if (ch == '"' || ch == '\\')
+ *w->buffer.curr++ = '\\';
+ *w->buffer.curr++ = ch;
+ }
+ *w->buffer.curr++ = '"';
+ }
+ else
+ {
+ sanei_w_space (w, 5);
+ *w->buffer.curr++ = '(';
+ *w->buffer.curr++ = 'n';
+ *w->buffer.curr++ = 'i';
+ *w->buffer.curr++ = 'l';
+ *w->buffer.curr++ = ')';
+ }
+ sanei_w_space (w, 1);
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ skip_ws (w);
+ sanei_w_space (w, 1);
+ ch = *w->buffer.curr++;
+ if (ch == '"')
+ {
+ alloced_len = len = 0;
+ str = 0;
+ done = 0;
+ do
+ {
+ sanei_w_space (w, 1);
+ if (w->status != 0)
+ return;
+
+ ch = *w->buffer.curr++;
+ if (ch == '"')
+ done = 1;
+
+ if (ch == '\\')
+ {
+ sanei_w_space (w, 1);
+ ch = *w->buffer.curr++;
+ }
+
+ if (len >= alloced_len)
+ {
+ alloced_len += 1024;
+ if (!str)
+ str = malloc (alloced_len);
+ else
+ str = realloc (str, alloced_len);
+
+ if (str == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return;
+ }
+ }
+ str[len++] = ch;
+ }
+ while (!done);
+
+ str[len - 1] = '\0';
+ *s = realloc (str, len);
+
+ if (*s == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ return;
+ }
+ }
+ else if (ch == '(')
+ {
+ sanei_w_space (w, 4);
+ if ( *w->buffer.curr++ != 'n'
+ || *w->buffer.curr++ != 'i'
+ || *w->buffer.curr++ != 'l'
+ || *w->buffer.curr++ != ')')
+ {
+ w->status = EINVAL;
+ return;
+ }
+ *s = 0;
+ }
+ else
+ {
+ w->status = EINVAL;
+ return;
+ }
+ break;
+
+ case WIRE_FREE:
+ if (*s)
+ free (*s);
+ break;
+ }
+}
+
+static void
+ascii_w_word (Wire *w, void *v)
+{
+ SANE_Word val, *word = v;
+ int i, is_negative = 0;
+ char buf[16];
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ val = *word;
+ i = sizeof (buf) - 1;
+ if (val < 0)
+ {
+ is_negative = 1;
+ val = -val;
+ }
+ do
+ {
+ buf[i--] = '0' + (val % 10);
+ val /= 10;
+ }
+ while (val);
+ if (is_negative)
+ buf[i--] = '-';
+
+ sanei_w_space (w, sizeof (buf) - i);
+ memcpy (w->buffer.curr, buf + i + 1, sizeof (buf) - i - 1);
+ w->buffer.curr += sizeof (buf) - i - 1;
+ *w->buffer.curr++ = '\n';
+ break;
+
+ case WIRE_DECODE:
+ skip_ws (w);
+ val = 0;
+ sanei_w_space (w, 1);
+ if (*w->buffer.curr == '-')
+ {
+ is_negative = 1;
+ ++w->buffer.curr;
+ }
+ while (1)
+ {
+ sanei_w_space (w, 1);
+ if (w->status != 0)
+ return;
+
+ if (!isdigit (*w->buffer.curr))
+ break;
+
+ val = 10*val + (*w->buffer.curr++ - '0');
+ }
+ *word = is_negative ? -val : val;
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+void
+sanei_codec_ascii_init (Wire *w)
+{
+ w->codec.w_byte = ascii_w_byte;
+ w->codec.w_char = ascii_w_char;
+ w->codec.w_word = ascii_w_word;
+ w->codec.w_string = ascii_w_string;
+}
diff --git a/sanei/sanei_codec_bin.c b/sanei/sanei_codec_bin.c
new file mode 100644
index 0000000..48cdead
--- /dev/null
+++ b/sanei/sanei_codec_bin.c
@@ -0,0 +1,139 @@
+/* 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_wire.h"
+#include "../include/sane/sanei_codec_bin.h"
+
+static void
+bin_w_byte (Wire *w, void *v)
+{
+ SANE_Byte *b = v;
+
+ sanei_w_space (w, 1);
+ if (w->status)
+ return;
+
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ *w->buffer.curr++ = *b;
+ break;
+
+ case WIRE_DECODE:
+ *b = *w->buffer.curr++;
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+static void
+bin_w_string (Wire *w, void *v)
+{
+ SANE_Word len;
+ SANE_String *s = v;
+
+ if (w->direction != WIRE_DECODE)
+ {
+ len = 0;
+ if (*s)
+ len = strlen (*s) + 1;
+ }
+ sanei_w_array (w, &len, v, w->codec.w_byte, 1);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (len == 0)
+ *s = 0;
+ else if (w->status == 0)
+ *(*s + len - 1) = '\0';
+ }
+}
+
+static void
+bin_w_word (Wire *w, void *v)
+{
+ SANE_Word val, *word = v;
+
+ sanei_w_space (w, 4);
+ if (w->status)
+ return;
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ val = *word;
+ /* store in bigendian byte-order: */
+ w->buffer.curr[0] = (val >> 24) & 0xff;
+ w->buffer.curr[1] = (val >> 16) & 0xff;
+ w->buffer.curr[2] = (val >> 8) & 0xff;
+ w->buffer.curr[3] = (val >> 0) & 0xff;
+ w->buffer.curr += 4;
+ break;
+
+ case WIRE_DECODE:
+ val = ( ((w->buffer.curr[0] & 0xff) << 24)
+ | ((w->buffer.curr[1] & 0xff) << 16)
+ | ((w->buffer.curr[2] & 0xff) << 8)
+ | ((w->buffer.curr[3] & 0xff) << 0));
+ *word = val;
+ w->buffer.curr += 4;
+ break;
+
+ case WIRE_FREE:
+ break;
+ }
+}
+
+void
+sanei_codec_bin_init (Wire *w)
+{
+ w->codec.w_byte = bin_w_byte;
+ w->codec.w_char = bin_w_byte;
+ w->codec.w_word = bin_w_word;
+ w->codec.w_string = bin_w_string;
+}
diff --git a/sanei/sanei_config.c b/sanei/sanei_config.c
new file mode 100644
index 0000000..c158766
--- /dev/null
+++ b/sanei/sanei_config.c
@@ -0,0 +1,453 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 Jeffrey S. Freedman
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file provides generic configuration support. */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/param.h>
+
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+
+#define BACKEND_NAME sanei_config
+#include "../include/sane/sanei_debug.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#if defined(_WIN32) || defined(HAVE_OS2_H)
+# define DIR_SEP ";"
+# define PATH_SEP '\\'
+#else
+# define DIR_SEP ":"
+# define PATH_SEP '/'
+#endif
+
+#define DEFAULT_DIRS "." DIR_SEP STRINGIFY(PATH_SANE_CONFIG_DIR)
+
+#ifdef __BEOS__
+#include <FindDirectory.h>
+#endif
+
+static char *dir_list;
+
+const char *
+sanei_config_get_paths ()
+{
+#ifdef __BEOS__
+ char result[PATH_MAX];
+#endif
+ void *mem;
+ char *dlist;
+ size_t len;
+
+ if (!dir_list)
+ {
+ DBG_INIT();
+
+ dlist = getenv ("SANE_CONFIG_DIR");
+ if (dlist)
+ dir_list = strdup (dlist);
+#ifdef __BEOS__
+ /* ~/config/settings/SANE takes precedence over /etc/sane.d/ */
+ if (!dir_list)
+ {
+ if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, true, result, PATH_MAX) == B_OK)
+ {
+ strcat(result,"/SANE");
+ strcat(result,DIR_SEP); /* do append the default ones */
+ dir_list = strdup (result);
+ }
+ }
+#endif
+ if (dir_list)
+ {
+ len = strlen (dir_list);
+ if ((len > 0) && (dir_list[len - 1] == DIR_SEP[0]))
+ {
+ /* append default search directories: */
+ mem = malloc (len + sizeof (DEFAULT_DIRS));
+ memcpy (mem, dir_list, len);
+ memcpy ((char *) mem + len, DEFAULT_DIRS, sizeof (DEFAULT_DIRS));
+ free (dir_list);
+ dir_list = mem;
+ }
+ }
+ else
+ {
+ /* Create a copy, since we might call free on it */
+ dir_list = strdup (DEFAULT_DIRS);
+ }
+ }
+ DBG (5, "sanei_config_get_paths: using config directories %s\n", dir_list);
+
+ return dir_list;
+}
+
+FILE *
+sanei_config_open (const char *filename)
+{
+ char *next, *dir, result[PATH_MAX];
+ const char *cfg_dir_list;
+ FILE *fp;
+ char *copy;
+
+ cfg_dir_list = sanei_config_get_paths ();
+ if (!cfg_dir_list)
+ {
+ DBG(2, "sanei_config_open: could not find config file `%s'\n", filename);
+ return NULL;
+ }
+
+ copy = strdup (cfg_dir_list);
+
+ for (next = copy; (dir = strsep (&next, DIR_SEP)) != 0; )
+ {
+ snprintf (result, sizeof (result), "%s%c%s", dir, PATH_SEP, filename);
+ DBG(4, "sanei_config_open: attempting to open `%s'\n", result);
+ fp = fopen (result, "r");
+ if (fp)
+ {
+ DBG(3, "sanei_config_open: using file `%s'\n", result);
+ break;
+ }
+ }
+ free (copy);
+
+ if (!fp)
+ DBG(2, "sanei_config_open: could not find config file `%s'\n", filename);
+
+ return fp;
+}
+
+const char *
+sanei_config_skip_whitespace (const char *str)
+{
+ while (str && *str && isspace (*str))
+ ++str;
+ return str;
+}
+
+const char *
+sanei_config_get_string (const char *str, char **string_const)
+{
+ const char *start;
+ size_t len;
+
+ str = sanei_config_skip_whitespace (str);
+
+ if (*str == '"')
+ {
+ start = ++str;
+ while (*str && *str != '"')
+ ++str;
+ len = str - start;
+ if (*str == '"')
+ ++str;
+ else
+ start = 0; /* final double quote is missing */
+ }
+ else
+ {
+ start = str;
+ while (*str && !isspace (*str))
+ ++str;
+ len = str - start;
+ }
+ if (start)
+ *string_const = strndup (start, len);
+ else
+ *string_const = 0;
+ return str;
+}
+
+char *
+sanei_config_read (char *str, int n, FILE *stream)
+{
+ char* rc;
+ char* start;
+ int len;
+
+ /* read line from stream */
+ rc = fgets( str, n, stream);
+ if (rc == NULL)
+ return NULL;
+
+ /* remove ending whitespaces */
+ len = strlen( str);
+ while( (0 < len) && (isspace( str[--len])) )
+ str[len] = '\0';
+
+ /* remove starting whitespaces */
+ start = str;
+ while( isspace( *start))
+ start++;
+
+ if (start != str)
+ do {
+ *str++ = *start++;
+ } while( *str);
+
+ return rc;
+}
+
+
+SANE_Status
+sanei_configure_attach (const char *config_file, SANEI_Config * config,
+ SANE_Status (*attach) (SANEI_Config * config,
+ const char *devname))
+{
+ SANE_Char line[PATH_MAX];
+ SANE_Char *token, *string;
+ SANE_Int len;
+ const char *lp, *lp2;
+ FILE *fp;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i, j, count;
+ void *value = NULL;
+ int size=0;
+ SANE_Bool found;
+ SANE_Word *wa;
+ SANE_Bool *ba;
+
+ DBG (3, "sanei_configure_attach: start\n");
+
+ /* open configuration file */
+ fp = sanei_config_open (config_file);
+ if (!fp)
+ {
+ DBG (2, "sanei_configure_attach: couldn't access %s\n", config_file);
+ DBG (3, "sanei_configure_attach: exit\n");
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ /* 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) && status == SANE_STATUS_GOOD)
+ {
+ /* skip white spaces at beginning of line */
+ lp = sanei_config_skip_whitespace (line);
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ /* skip comment line */
+ if (line[0] == '#')
+ continue;
+
+ len = strlen (line);
+
+ /* delete newline characters at end */
+ if (line[len - 1] == '\n')
+ line[--len] = '\0';
+
+ lp2 = lp;
+
+ /* to ensure maximum compatibility, we accept line like:
+ * option "option_name" "option_value"
+ * "option_name" "option_value"
+ * So we parse the line 2 time to find an option */
+ /* check if it is an option */
+ lp = sanei_config_get_string (lp, &token);
+ if (strncmp (token, "option", 6) == 0)
+ {
+ /* skip the "option" token */
+ free (token);
+ lp = sanei_config_get_string (lp, &token);
+ }
+
+ /* search for a matching descriptor */
+ i = 0;
+ found = SANE_FALSE;
+ while (config!=NULL && i < config->count && !found)
+ {
+ if (strcmp (config->descriptors[i]->name, token) == 0)
+ {
+ found = SANE_TRUE;
+ switch (config->descriptors[i]->type)
+ {
+ case SANE_TYPE_INT:
+ size=config->descriptors[i]->size;
+ value = malloc (size);
+ wa = (SANE_Word *) value;
+ count = config->descriptors[i]->size / sizeof (SANE_Word);
+ for (j = 0; j < count; j++)
+ {
+ lp = sanei_config_get_string (lp, &string);
+ if (string == NULL)
+ {
+ DBG (2,
+ "sanei_configure_attach: couldn't find a string to parse");
+ return SANE_STATUS_INVAL;
+ }
+ wa[j] = strtol (string, NULL, 0);
+ free (string);
+ }
+ break;
+ case SANE_TYPE_BOOL:
+ size=config->descriptors[i]->size;
+ value = malloc (size);
+ ba = (SANE_Bool *) value;
+ count = config->descriptors[i]->size / sizeof (SANE_Bool);
+ for (j = 0; j < count; j++)
+ {
+ lp = sanei_config_get_string (lp, &string);
+ if (string == NULL)
+ {
+ DBG (2,
+ "sanei_configure_attach: couldn't find a string to parse");
+ return SANE_STATUS_INVAL;
+ }
+ if ((strcmp (string, "1") == 0)
+ || (strcmp (string, "true") == 0))
+ {
+ ba[j] = SANE_TRUE;
+ }
+ else
+ {
+ if ((strcmp (string, "0") == 0)
+ || (strcmp (string, "false") == 0))
+ ba[j] = SANE_FALSE;
+ else
+ {
+ DBG (2,
+ "sanei_configure_attach: couldn't find a valid boolean value");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ free (string);
+ }
+ break;
+ case SANE_TYPE_FIXED:
+ size=config->descriptors[i]->size;
+ value = malloc (size);
+ wa = (SANE_Word *) value;
+ count = config->descriptors[i]->size / sizeof (SANE_Word);
+ for (j = 0; j < count; j++)
+ {
+ lp = sanei_config_get_string (lp, &string);
+ if (string == NULL)
+ {
+ DBG (2,
+ "sanei_configure_attach: couldn't find a string to parse");
+ return SANE_STATUS_INVAL;
+ }
+ wa[j] = SANE_FIX(strtod (string, NULL));
+ free (string);
+ }
+ break;
+ case SANE_TYPE_STRING:
+ sanei_config_get_string (lp, &string);
+ if (string == NULL)
+ {
+ DBG (2,
+ "sanei_configure_attach: couldn't find a string value to parse");
+ return SANE_STATUS_INVAL;
+ }
+ value = string;
+ size=strlen(string)+1;
+ if(size>config->descriptors[i]->size)
+ {
+ size=config->descriptors[i]->size-1;
+ string[size]=0;
+ }
+ break;
+ default:
+ DBG (1,
+ "sanei_configure_attach: incorrect type %d for option %s, skipping option ...\n",
+ config->descriptors[i]->type,
+ config->descriptors[i]->name);
+ }
+
+ /* check decoded value */
+ status = sanei_check_value (config->descriptors[i], value);
+
+ /* if value OK, copy it in configuration struct */
+ if (status == SANE_STATUS_GOOD)
+ {
+ memcpy (config->values[i], value, size);
+ }
+ if (value != NULL)
+ {
+ free (value);
+ value = NULL;
+ }
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_configure_attach: failed to parse option '%s', line '%s'\n",
+ token, line);
+ }
+ i++;
+ }
+ free (token);
+
+ /* not detected as an option, so we call the attach function
+ * with it */
+ if (!found && status == SANE_STATUS_GOOD)
+ {
+ /* if not an option, try to attach */
+ /* to avoid every backend to depend on scsi and usb functions
+ * we call back the backend for attach. In turn it will call
+ * sanei_usb_attach_matching_devices, sanei_config_attach_matching_devices
+ * or other. This means 2 callback functions per backend using this
+ * function. */
+ DBG (3, "sanei_configure_attach: trying to attach with '%s'\n",
+ lp2);
+ if(attach!=NULL)
+ attach (config, lp2);
+ }
+ }
+
+ fclose (fp);
+ DBG (3, "sanei_configure_attach: exit\n");
+ return status;
+}
diff --git a/sanei/sanei_config2.c b/sanei/sanei_config2.c
new file mode 100644
index 0000000..44c6b93
--- /dev/null
+++ b/sanei/sanei_config2.c
@@ -0,0 +1,154 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998 David Mosberger
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file provides generic configuration support. */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __BEOS__
+#include <dirent.h>
+#include <unistd.h>
+#include <drivers/USB_scanner.h>
+#endif
+
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_scsi.h"
+
+/* This logically belongs to sanei_config.c but not every backend that
+ uses sanei_config() wants to depend on this function. */
+
+void
+sanei_config_attach_matching_devices (const char *name,
+ SANE_Status (*attach) (const char *dev))
+{
+ int bus = -1, channel = -1, id = -1, lun = -1;
+ char *vendor = 0, *model = 0, *type = 0, *end;
+
+ if (strncmp (name, "scsi", 4) == 0)
+ {
+ name += 4;
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &vendor);
+ if (vendor && strcmp (vendor, "*") == 0)
+ {
+ free (vendor);
+ vendor = 0;
+ }
+ name = sanei_config_skip_whitespace (name);
+ }
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &model);
+ if (model && strcmp (model, "*") == 0)
+ {
+ free (model);
+ model = 0;
+ }
+ name = sanei_config_skip_whitespace (name);
+ }
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &type);
+ if (type && strcmp (type, "*") == 0)
+ {
+ free (type);
+ type = 0;
+ }
+ name = sanei_config_skip_whitespace (name);
+ }
+
+ if (isdigit (*name))
+ {
+ bus = strtol (name, &end, 10);
+ name = sanei_config_skip_whitespace (end);
+ }
+ else if (*name == '*')
+ name = sanei_config_skip_whitespace (++name);
+
+ if (isdigit (*name))
+ {
+ channel = strtol (name, &end, 10);
+ name = sanei_config_skip_whitespace (end);
+ }
+ else if (*name == '*')
+ name = sanei_config_skip_whitespace (++name);
+
+ if (isdigit (*name))
+ {
+ id = strtol (name, &end, 10);
+ name = sanei_config_skip_whitespace (end);
+ }
+ else if (*name == '*')
+ name = sanei_config_skip_whitespace (++name);
+
+ if (isdigit (*name))
+ {
+ lun = strtol (name, &end, 10);
+ name = sanei_config_skip_whitespace (end);
+ }
+ else if (*name == '*')
+ name = sanei_config_skip_whitespace (++name);
+
+ sanei_scsi_find_devices (vendor, model, type, bus, channel, id, lun,
+ attach);
+
+ if (vendor)
+ free (vendor);
+ if (model)
+ free (model);
+ if (type)
+ free (type);
+ }
+ else
+ (*attach) (name);
+}
diff --git a/sanei/sanei_constrain_value.c b/sanei/sanei_constrain_value.c
new file mode 100644
index 0000000..8f601cc
--- /dev/null
+++ b/sanei/sanei_constrain_value.c
@@ -0,0 +1,309 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Beck
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include <stdio.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+
+SANE_Status
+sanei_check_value (const SANE_Option_Descriptor * opt, void *value)
+{
+ const SANE_String_Const *string_list;
+ const SANE_Word *word_list;
+ int i, count;
+ const SANE_Range *range;
+ SANE_Word w, v, *array;
+ SANE_Bool *barray;
+ size_t len;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+
+ /* single values are treated as arrays of length 1 */
+ array = (SANE_Word *) value;
+
+ /* compute number of elements */
+ if (opt->size > 0)
+ {
+ count = opt->size / sizeof (SANE_Word);
+ }
+ else
+ {
+ count = 1;
+ }
+
+ range = opt->constraint.range;
+ /* for each element of the array, we check according to the constraint */
+ for (i = 0; i < count; i++)
+ {
+ /* test for min and max */
+ if (array[i] < range->min || array[i] > range->max)
+ return SANE_STATUS_INVAL;
+
+ /* check quantization */
+ if (range->quant)
+ {
+ v =
+ (unsigned int) (array[i] - range->min +
+ range->quant / 2) / range->quant;
+ v = v * range->quant + range->min;
+ if (v != array[i])
+ return SANE_STATUS_INVAL;
+ }
+ }
+ 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])
+ return SANE_STATUS_INVAL;
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ string_list = opt->constraint.string_list;
+ len = strlen (value);
+
+ for (i = 0; string_list[i]; ++i)
+ if (strncmp (value, string_list[i], len) == 0
+ && len == strlen (string_list[i]))
+ return SANE_STATUS_GOOD;
+ return SANE_STATUS_INVAL;
+
+ case SANE_CONSTRAINT_NONE:
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ /* single values are treated as arrays of length 1 */
+ array = (SANE_Word *) value;
+
+ /* compute number of elements */
+ if (opt->size > 0)
+ {
+ count = opt->size / sizeof (SANE_Bool);
+ }
+ else
+ {
+ count = 1;
+ }
+
+ barray = (SANE_Bool *) value;
+
+ /* test each boolean value in the array */
+ for (i = 0; i < count; i++)
+ {
+ if (barray[i] != SANE_TRUE && barray[i] != SANE_FALSE)
+ return SANE_STATUS_INVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * This function apply the constraint defined by the option descriptor
+ * to the given value, and update the info flags holder if needed. It
+ * return SANE_STATUS_INVAL if the constraint cannot be applied, else
+ * it returns SANE_STATUS_GOOD.
+ */
+SANE_Status
+sanei_constrain_value (const SANE_Option_Descriptor * opt, void *value,
+ SANE_Word * info)
+{
+ const SANE_String_Const *string_list;
+ const SANE_Word *word_list;
+ int i, k, num_matches, match;
+ const SANE_Range *range;
+ SANE_Word w, v, *array;
+ SANE_Bool b;
+ size_t len;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+
+ /* single values are treated as arrays of length 1 */
+ array = (SANE_Word *) value;
+
+ /* compute number of elements */
+ if (opt->size > 0)
+ {
+ k = opt->size / sizeof (SANE_Word);
+ }
+ else
+ {
+ k = 1;
+ }
+
+ range = opt->constraint.range;
+ /* for each element of the array, we apply the constraint */
+ for (i = 0; i < k; i++)
+ {
+ /* constrain min */
+ if (array[i] < range->min)
+ {
+ array[i] = range->min;
+ if (info)
+ {
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+
+ /* constrain max */
+ if (array[i] > range->max)
+ {
+ array[i] = range->max;
+ if (info)
+ {
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+
+ /* quantization */
+ if (range->quant)
+ {
+ v =
+ (unsigned int) (array[i] - range->min +
+ range->quant / 2) / range->quant;
+ v = v * range->quant + range->min;
+ /* due to rounding issues with sane 'fixed' values,
+ * the computed value may exceed max */
+ if (v > range->max)
+ {
+ v = range->max;
+ }
+ if (v != array[i])
+ {
+ array[i] = v;
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ }
+ }
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ /* If there is no exact match in the list, use the nearest value */
+ w = *(SANE_Word *) value;
+ word_list = opt->constraint.word_list;
+ for (i = 1, k = 1, v = abs (w - word_list[1]); i <= word_list[0]; i++)
+ {
+ SANE_Word vh;
+ if ((vh = abs (w - word_list[i])) < v)
+ {
+ v = vh;
+ k = i;
+ }
+ }
+ if (w != word_list[k])
+ {
+ *(SANE_Word *) value = word_list[k];
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ }
+ 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]);
+ return SANE_STATUS_GOOD;
+ }
+ ++num_matches;
+ }
+
+ if (num_matches > 1)
+ return SANE_STATUS_INVAL;
+ else if (num_matches == 1)
+ {
+ strcpy (value, string_list[match]);
+ return SANE_STATUS_GOOD;
+ }
+ return SANE_STATUS_INVAL;
+
+ case SANE_CONSTRAINT_NONE:
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ b = *(SANE_Bool *) value;
+ if (b != SANE_TRUE && b != SANE_FALSE)
+ return SANE_STATUS_INVAL;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ return SANE_STATUS_GOOD;
+}
diff --git a/sanei/sanei_init_debug.c b/sanei/sanei_init_debug.c
new file mode 100644
index 0000000..bb5b755
--- /dev/null
+++ b/sanei/sanei_init_debug.c
@@ -0,0 +1,150 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Beck
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdarg.h>
+#ifdef HAVE_VSYSLOG
+#include <syslog.h>
+#endif
+#ifdef HAVE_OS2_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <sys/stat.h>
+
+#ifdef HAVE_OS2_H
+# define INCL_DOS
+# include <os2.h>
+#endif
+
+#define BACKEND_NAME sanei_debug
+#include "../include/sane/sanei_debug.h"
+
+/* If a frontend enables translations, the system toupper()
+ * call will use the LANG env var. We need to use ascii
+ * instead, so the debugging env var name matches the docs.
+ * This is a particular problem in Turkish, where 'i' does
+ * not capitalize to 'I' */
+char
+toupper_ascii (int c)
+{
+ if(c > 0x60 && c < 0x7b)
+ return c - 0x20;
+ return c;
+}
+
+void
+sanei_init_debug (const char * backend, int * var)
+{
+ char ch, buf[256] = "SANE_DEBUG_";
+ const char * val;
+ unsigned int i;
+
+ *var = 0;
+
+ for (i = 11; (ch = backend[i - 11]) != 0; ++i)
+ {
+ if (i >= sizeof (buf) - 1)
+ break;
+ buf[i] = toupper_ascii(ch);
+ }
+ buf[i] = '\0';
+
+ val = getenv (buf);
+
+ if (!val)
+ return;
+
+ *var = atoi (val);
+
+ DBG (0, "Setting debug level of %s to %d.\n", backend, *var);
+}
+
+void
+sanei_debug_msg
+ (int level, int max_level, const char *be, const char *fmt, va_list ap)
+{
+ char *msg;
+
+ if (max_level >= level)
+ {
+#ifdef S_IFSOCK
+ if ( 1 == isfdtype(fileno(stderr), S_IFSOCK) )
+ {
+ msg = (char *)malloc (sizeof(char) * (strlen(be) + strlen(fmt) + 4));
+ if (msg == NULL)
+ {
+ syslog (LOG_DEBUG, "[sanei_debug] malloc() failed\n");
+ vsyslog (LOG_DEBUG, fmt, ap);
+ }
+ else
+ {
+ sprintf (msg, "[%s] %s", be, fmt);
+ vsyslog(LOG_DEBUG, msg, ap);
+ free (msg);
+ }
+ }
+ else
+#endif
+ {
+ fprintf (stderr, "[%s] ", be);
+ vfprintf (stderr, fmt, ap);
+ }
+
+ }
+}
+
+#ifdef NDEBUG
+void
+sanei_debug_ndebug (int level, const char *fmt, ...)
+{
+ /* this function is never called */
+}
+#endif
diff --git a/sanei/sanei_jpeg.c b/sanei/sanei_jpeg.c
new file mode 100644
index 0000000..7b66dae
--- /dev/null
+++ b/sanei/sanei_jpeg.c
@@ -0,0 +1,235 @@
+/*
+ Code for JPEG decompression by the Independent JPEG Group. Please see
+ README.jpeg in the top level directory.
+ */
+
+#include "../include/sane/config.h"
+
+#ifdef HAVE_LIBJPEG
+
+#include "../include/sane/sanei_jpeg.h"
+
+typedef struct
+ {
+ struct djpeg_dest_struct pub; /* public fields */
+
+ /* Usually these two pointers point to the same place: */
+ char *iobuffer; /* fwrite's I/O buffer */
+ JSAMPROW pixrow; /* decompressor output buffer */
+ size_t buffer_width; /* width of I/O buffer */
+ JDIMENSION samples_per_row; /* JSAMPLEs per output row */
+ }
+ppm_dest_struct;
+
+typedef ppm_dest_struct *ppm_dest_ptr;
+
+/*
+ * For 12-bit JPEG data, we either downscale the values to 8 bits
+ * (to write standard byte-per-sample PPM/PGM files), or output
+ * nonstandard word-per-sample PPM/PGM files. Downscaling is done
+ * if PPM_NORAWWORD is defined (this can be done in the Makefile
+ * or in jconfig.h).
+ * (When the core library supports data precision reduction, a cleaner
+ * implementation will be to ask for that instead.)
+ */
+
+#if BITS_IN_JSAMPLE==8
+#define PUTPPMSAMPLE(ptr,v) *ptr++=(char) (v)
+#define BYTESPERSAMPLE 1
+#define PPM_MAXVAL 255
+#else
+#ifdef PPM_NORAWWORD
+#define PUTPPMSAMPLE(ptr,v) *ptr++=(char) ((v) >> (BITS_IN_JSAMPLE-8))
+#define BYTESPERSAMPLE 1
+#define PPM_MAXVAL 255
+#else
+/* The word-per-sample format always puts the LSB first. */
+#define PUTPPMSAMPLE(ptr,v) \
+ { register int val_=v; \
+ *ptr++=(char) (val_ & 0xFF); \
+ *ptr++=(char) ((val_ >> 8) & 0xFF); \
+ }
+#define BYTESPERSAMPLE 2
+#define PPM_MAXVAL ((1<<BITS_IN_JSAMPLE)-1)
+#endif
+#endif
+
+METHODDEF (void)
+sanei_jpeg_start_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
+{
+ cinfo = cinfo;
+ dinfo = dinfo;
+ /* header image is supplied for us */
+
+}
+
+METHODDEF (void)
+sanei_jpeg_finish_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
+{
+ cinfo = cinfo;
+ dinfo = dinfo;
+
+ /* nothing to do */
+}
+
+/*
+ * Write some pixel data.
+ * In this module rows_supplied will always be 1.
+ *
+ * put_pixel_rows handles the "normal" 8-bit case where the decompressor
+ * output buffer is physically the same as the fwrite buffer.
+ */
+
+METHODDEF (void)
+sanei_jpeg_put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
+ JDIMENSION rows_supplied, char *data)
+{
+ ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
+ cinfo = cinfo;
+ dinfo = dinfo;
+ rows_supplied = rows_supplied;
+
+ memcpy (data, dest->iobuffer, dest->buffer_width);
+}
+
+
+/*
+ * This code is used when we have to copy the data and apply a pixel
+ * format translation. Typically this only happens in 12-bit mode.
+ */
+
+METHODDEF (void)
+sanei_jpeg_copy_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
+ JDIMENSION rows_supplied, char *data)
+{
+ ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
+ register char *bufferptr;
+ register JSAMPROW ptr;
+ register JDIMENSION col;
+
+ cinfo = cinfo;
+ dinfo = dinfo;
+ rows_supplied = rows_supplied;
+
+ ptr = dest->pub.buffer[0];
+ bufferptr = dest->iobuffer;
+ for (col = dest->samples_per_row; col > 0; col--)
+ {
+ PUTPPMSAMPLE (bufferptr, GETJSAMPLE (*ptr++));
+ }
+ memcpy (data, dest->iobuffer, dest->buffer_width);
+}
+
+
+/*
+ * Write some pixel data when color quantization is in effect.
+ * We have to demap the color index values to straight data.
+ */
+
+METHODDEF (void)
+sanei_jpeg_put_demapped_rgb (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
+ JDIMENSION rows_supplied, char *data)
+{
+
+ ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
+ register char *bufferptr;
+ register int pixval;
+ register JSAMPROW ptr;
+ register JSAMPROW color_map0 = cinfo->colormap[0];
+ register JSAMPROW color_map1 = cinfo->colormap[1];
+ register JSAMPROW color_map2 = cinfo->colormap[2];
+ register JDIMENSION col;
+
+ rows_supplied = rows_supplied;
+
+ ptr = dest->pub.buffer[0];
+ bufferptr = dest->iobuffer;
+ for (col = cinfo->output_width; col > 0; col--)
+ {
+ pixval = GETJSAMPLE (*ptr++);
+ PUTPPMSAMPLE (bufferptr, GETJSAMPLE (color_map0[pixval]));
+ PUTPPMSAMPLE (bufferptr, GETJSAMPLE (color_map1[pixval]));
+ PUTPPMSAMPLE (bufferptr, GETJSAMPLE (color_map2[pixval]));
+ }
+ memcpy (data, dest->iobuffer, dest->buffer_width);
+}
+
+
+METHODDEF (void)
+sanei_jpeg_put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
+ JDIMENSION rows_supplied, char *data)
+{
+ ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
+ register char *bufferptr;
+ register JSAMPROW ptr;
+ register JSAMPROW color_map = cinfo->colormap[0];
+ register JDIMENSION col;
+
+ rows_supplied = rows_supplied;
+
+ ptr = dest->pub.buffer[0];
+ bufferptr = dest->iobuffer;
+ for (col = cinfo->output_width; col > 0; col--)
+ {
+ PUTPPMSAMPLE (bufferptr,
+ GETJSAMPLE (color_map[GETJSAMPLE (*ptr++)]));
+ }
+ memcpy (data, dest->iobuffer, dest->buffer_width);
+}
+
+GLOBAL (djpeg_dest_ptr)
+sanei_jpeg_jinit_write_ppm (j_decompress_ptr cinfo)
+{
+
+ ppm_dest_ptr dest;
+
+ /* Create module interface object, fill in method pointers */
+ dest = (ppm_dest_ptr)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+ SIZEOF (ppm_dest_struct));
+ dest->pub.start_output = sanei_jpeg_start_output_ppm;
+ dest->pub.finish_output = sanei_jpeg_finish_output_ppm;
+
+ /* Calculate output image dimensions so we can allocate space */
+ jpeg_calc_output_dimensions (cinfo);
+
+ /* Create physical I/O buffer. Note we make this near on a PC. */
+ dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
+ dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * SIZEOF (char));
+ dest->iobuffer = (char *) (*cinfo->mem->alloc_small)
+ ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width);
+
+ if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 ||
+ SIZEOF (JSAMPLE) != SIZEOF (char))
+ {
+ /* When quantizing, we need an output buffer for clrmap indexes
+ * that's separate from the physical I/O buffer. We also need a
+ * separate buffer if pixel format translation must take place.
+ */
+ dest->pub.buffer = (*cinfo->mem->alloc_sarray)
+ ((j_common_ptr) cinfo, JPOOL_IMAGE,
+ cinfo->output_width * cinfo->output_components,
+ (JDIMENSION) 1);
+ dest->pub.buffer_height = 1;
+ if (!cinfo->quantize_colors)
+ dest->pub.put_pixel_rows = sanei_jpeg_copy_pixel_rows;
+ else if (cinfo->out_color_space == JCS_GRAYSCALE)
+ dest->pub.put_pixel_rows = sanei_jpeg_put_demapped_gray;
+ else
+ dest->pub.put_pixel_rows = sanei_jpeg_put_demapped_rgb;
+ }
+ else
+ {
+ /* We will fwrite() directly from decompressor output buffer. */
+ /* Synthesize a JSAMPARRAY pointer structure */
+ /* Cast here implies near->far pointer conversion on PCs */
+ dest->pixrow = (JSAMPROW) dest->iobuffer;
+ dest->pub.buffer = &dest->pixrow;
+ dest->pub.buffer_height = 1;
+ dest->pub.put_pixel_rows = sanei_jpeg_put_pixel_rows;
+ }
+
+ return (djpeg_dest_ptr) dest;
+}
+
+#endif
diff --git a/sanei/sanei_lm983x.c b/sanei/sanei_lm983x.c
new file mode 100644
index 0000000..5b41985
--- /dev/null
+++ b/sanei/sanei_lm983x.c
@@ -0,0 +1,265 @@
+/* sane - Scanner Access Now Easy.
+
+ based on sources acquired from Plustek Inc.
+ Copyright (C) 2002-2004 Gerhard Jaeger <gerhard@gjaeger.de>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Interface files for the LM9831/2/3 chip,
+ a chip used in many USB scanners.
+
+ */
+
+#include "../include/sane/config.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#define BACKEND_NAME sanei_lm983x /**< the name of this module for dbg */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_lm983x.h"
+
+/***************************** some definitions ******************************/
+
+#define _MIN(a,b) ((a) < (b) ? (a) : (b))
+#define _MAX(a,b) ((a) > (b) ? (a) : (b))
+
+#define _CMD_BYTE_CNT 4 /**< header for LM983x transfers */
+#define _MAX_RETRY 20 /**< number of tries for reset */
+#define _LM9831_MAX_REG 0x7f /**< number of LM983x bytes */
+#define _MAX_TRANSFER_SIZE 60 /**< max. number of bytes to transfer */
+
+/******************************* the functions *******************************/
+
+void
+sanei_lm983x_init( void )
+{
+ DBG_INIT();
+}
+
+SANE_Status
+sanei_lm983x_write_byte( SANE_Int fd, SANE_Byte reg, SANE_Byte value )
+{
+ return sanei_lm983x_write( fd, reg, &value, 1, SANE_FALSE );
+}
+
+SANE_Status
+sanei_lm983x_write( SANE_Int fd, SANE_Byte reg,
+ SANE_Byte *buffer, SANE_Word len, SANE_Bool increment )
+{
+ size_t size;
+ SANE_Byte command_buffer[_MAX_TRANSFER_SIZE + _CMD_BYTE_CNT];
+ SANE_Status result;
+ SANE_Word bytes, max_len;
+
+ DBG( 15, "sanei_lm983x_write: fd=%d, reg=%d, len=%d, increment=%d\n", fd,
+ reg, len, increment);
+
+ if( reg > _LM9831_MAX_REG ) {
+ DBG( 1, "sanei_lm983x_write: register out of range (%u>%u)\n",
+ reg, _LM9831_MAX_REG );
+ return SANE_STATUS_INVAL;
+ }
+
+ for( bytes = 0; len > 0; ) {
+
+ max_len = _MIN( len, _MAX_TRANSFER_SIZE );
+
+ command_buffer[0] = 0; /* write */
+ command_buffer[1] = reg; /* LM983x register */
+
+ if( increment == SANE_TRUE ) {
+ command_buffer[0] += 0x02; /* increase reg? */
+ command_buffer[1] += bytes;
+ }
+
+ command_buffer[2] = (max_len >> 8) & 0xff; /* bytes to write MSB */
+ command_buffer[3] = max_len & 0xff; /* bytes to write LSB */
+
+ memcpy( command_buffer + _CMD_BYTE_CNT, buffer + bytes, max_len );
+
+ size = (max_len + _CMD_BYTE_CNT);
+ result = sanei_usb_write_bulk( fd, command_buffer, &size );
+
+ if( SANE_STATUS_GOOD != result )
+ return result;
+
+ if( size != (size_t)(max_len + _CMD_BYTE_CNT)) {
+ DBG( 2, "sanei_lm983x_write: short write (%d/%d)\n",
+ result, max_len + _CMD_BYTE_CNT);
+
+ if( size < _CMD_BYTE_CNT ) {
+ DBG( 1, "sanei_lm983x_write: couldn't even send command\n" );
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG( 1, "sanei_lm983x_write: trying again\n" );
+ }
+ len -= (size - _CMD_BYTE_CNT);
+ bytes += (size - _CMD_BYTE_CNT);
+ }
+ DBG( 15, "sanei_lm983x_write: succeeded\n" );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_lm983x_read( SANE_Int fd, SANE_Byte reg,
+ SANE_Byte *buffer, SANE_Word len, SANE_Bool increment )
+{
+ size_t size;
+ SANE_Byte command_buffer[_CMD_BYTE_CNT];
+ SANE_Status result;
+ SANE_Word bytes, max_len, read_bytes;
+
+ DBG( 15, "sanei_lm983x_read: fd=%d, reg=%d, len=%d, increment=%d\n", fd,
+ reg, len, increment );
+ if( reg > _LM9831_MAX_REG ) {
+ DBG( 1, "sanei_lm983x_read: register out of range (%u>%u)\n",
+ reg, _LM9831_MAX_REG );
+ return SANE_STATUS_INVAL;
+ }
+
+ for( bytes = 0; len > 0; ) {
+
+ max_len = _MIN(len, 0xFFFF );
+ command_buffer[0] = 1; /* read */
+ command_buffer[1] = reg; /* LM9831 register */
+
+ if( increment ) {
+ command_buffer[0] += 0x02;
+ command_buffer[1] += bytes;
+ }
+
+ command_buffer[2] = (max_len >> 8) & 0xff; /* bytes to read MSB */
+ command_buffer[3] = max_len & 0xff; /* bytes to read LSB */
+
+ DBG( 15, "sanei_lm983x_read: writing command: "
+ "%02x %02x %02x %02x\n", command_buffer[0], command_buffer[1],
+ command_buffer[2], command_buffer[3]);
+
+ size = _CMD_BYTE_CNT;
+ result = sanei_usb_write_bulk( fd, command_buffer, &size );
+
+ if( SANE_STATUS_GOOD != result )
+ return result;
+
+ if( size != _CMD_BYTE_CNT) {
+ DBG( 1, "sanei_lm983x_read: short write while writing command "
+ "(%d/_CMD_BYTE_CNT)\n", result);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ read_bytes = 0;
+ do {
+
+ size = (max_len - read_bytes);
+
+ result = sanei_usb_read_bulk( fd, (buffer + bytes + read_bytes), &size );
+
+ if( SANE_STATUS_GOOD != result )
+ return result;
+
+ read_bytes += size;
+ DBG( 15, "sanei_lm983x_read: read %lu bytes\n", (u_long) size );
+
+ if( read_bytes != max_len ) {
+ DBG( 2, "sanei_lm983x_read: short read (%d/%d)\n",
+ result, max_len );
+ /* wait a little bit before retrying */
+ usleep( 10000 );
+ DBG( 2, "sanei_lm983x_read: trying again\n" );
+ }
+ } while( read_bytes < max_len );
+
+ bytes += (max_len);
+ len -= (max_len);
+ }
+ DBG( 15, "sanei_lm983x_read: succeeded\n" );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Bool sanei_lm983x_reset( SANE_Int fd )
+{
+ SANE_Status res;
+ SANE_Byte tmp;
+ SANE_Int i;
+
+ DBG( 15, "sanei_lm983x_reset()\n" );
+
+ for( i = 0; i < _MAX_RETRY; i++ ) {
+
+ /* Read the command register and check that the reset bit is not set
+ * If it is set, clear it and return false to indicate that
+ * the bit has only now been cleared
+ *
+ * Write the command bytes for a register read
+ * without increment
+ */
+ if( SANE_STATUS_GOOD != sanei_lm983x_read_byte( fd, 0x07, &tmp ))
+ continue;
+
+ if( tmp & 0x20 ) {
+
+ res = sanei_lm983x_write_byte( fd, 0x07, 0 );
+
+ /* We will attempt to reset it but we really don't do
+ * anything if this fails
+ */
+ if( res == SANE_STATUS_GOOD ) {
+ DBG( 15, "Resetting the LM983x already done\n" );
+ return SANE_TRUE;
+ }
+ } else {
+
+ res = sanei_lm983x_write_byte( fd, 0x07, 0x20 );
+ if( res == SANE_STATUS_GOOD ) {
+ DBG( 15, "Resetting the LM983x done\n" );
+ return SANE_TRUE;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+/* END sanei_lm983x.c .......................................................*/
diff --git a/sanei/sanei_magic.c b/sanei/sanei_magic.c
new file mode 100644
index 0000000..167d7dd
--- /dev/null
+++ b/sanei/sanei_magic.c
@@ -0,0 +1,1941 @@
+/*
+ * sanei_magic - Image processing functions for despeckle, deskew, and autocrop
+
+ Copyright (C) 2009 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+ */
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#define BACKEND_NAME sanei_magic /* name of this module for debugging */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_magic.h"
+
+/* prototypes for utility functions defined at bottom of file */
+int * sanei_magic_getTransY (
+ SANE_Parameters * params, int dpi, SANE_Byte * buffer, int top);
+
+int * sanei_magic_getTransX (
+ SANE_Parameters * params, int dpi, SANE_Byte * buffer, int left);
+
+static SANE_Status getTopEdge (int width, int height, int resolution,
+ int * buff, double * finSlope, int * finXInter, int * finYInter);
+
+static SANE_Status getLeftEdge (int width, int height, int * top, int * bot,
+ double slope, int * finXInter, int * finYInter);
+
+static SANE_Status getLine (int height, int width, int * buff,
+ int slopes, double minSlope, double maxSlope,
+ int offsets, int minOffset, int maxOffset,
+ double * finSlope, int * finOffset, int * finDensity);
+
+void
+sanei_magic_init( void )
+{
+ DBG_INIT();
+}
+
+/* find small spots and replace them with image background color */
+SANE_Status
+sanei_magic_despeck (SANE_Parameters * params, SANE_Byte * buffer,
+ SANE_Int diam)
+{
+
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int pw = params->pixels_per_line;
+ int bw = params->bytes_per_line;
+ int h = params->lines;
+ int bt = bw*h;
+
+ int i,j,k,l,n;
+
+ DBG (10, "sanei_magic_despeck: start\n");
+
+ if(params->format == SANE_FRAME_RGB){
+
+ for(i=bw; i<bt-bw-(bw*diam); i+=bw){
+ for(j=1; j<pw-1-diam; j++){
+
+ int thresh = 255*3;
+ int outer[] = {0,0,0};
+ int hits = 0;
+
+ /* loop over rows and columns in window */
+ /* find darkest pixel */
+ for(k=0; k<diam; k++){
+ for(l=0; l<diam; l++){
+ int tmp = 0;
+
+ for(n=0; n<3; n++){
+ tmp += buffer[i + j*3 + k*bw + l*3 + n];
+ }
+
+ if(tmp < thresh)
+ thresh = tmp;
+ }
+ }
+
+ /* convert darkest pixel into a brighter threshold */
+ thresh = (thresh + 255*3 + 255*3)/3;
+
+ /*loop over rows and columns around window */
+ for(k=-1; k<diam+1; k++){
+ for(l=-1; l<diam+1; l++){
+
+ int tmp[3];
+
+ /* dont count pixels in the window */
+ if(k != -1 && k != diam && l != -1 && l != diam)
+ continue;
+
+ for(n=0; n<3; n++){
+ tmp[n] = buffer[i + j*3 + k*bw + l*3 + n];
+ outer[n] += tmp[n];
+ }
+ if(tmp[0]+tmp[1]+tmp[2] < thresh){
+ hits++;
+ break;
+ }
+ }
+ }
+
+ /*no hits, overwrite with avg surrounding color*/
+ if(!hits){
+
+ /* per channel replacement color */
+ for(n=0; n<3; n++){
+ outer[n] /= (4*diam + 4);
+ }
+
+ for(k=0; k<diam; k++){
+ for(l=0; l<diam; l++){
+ for(n=0; n<3; n++){
+ buffer[i + j*3 + k*bw + l*3 + n] = outer[n];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 8){
+ for(i=bw; i<bt-bw-(bw*diam); i+=bw){
+ for(j=1; j<pw-1-diam; j++){
+
+ int thresh = 255;
+ int outer = 0;
+ int hits = 0;
+
+ for(k=0; k<diam; k++){
+ for(l=0; l<diam; l++){
+ if(buffer[i + j + k*bw + l] < thresh)
+ thresh = buffer[i + j + k*bw + l];
+ }
+ }
+
+ /* convert darkest pixel into a brighter threshold */
+ thresh = (thresh + 255 + 255)/3;
+
+ /*loop over rows and columns around window */
+ for(k=-1; k<diam+1; k++){
+ for(l=-1; l<diam+1; l++){
+
+ int tmp = 0;
+
+ /* dont count pixels in the window */
+ if(k != -1 && k != diam && l != -1 && l != diam)
+ continue;
+
+ tmp = buffer[i + j + k*bw + l];
+
+ if(tmp < thresh){
+ hits++;
+ break;
+ }
+
+ outer += tmp;
+ }
+ }
+
+ /*no hits, overwrite with avg surrounding color*/
+ if(!hits){
+ /* replacement color */
+ outer /= (4*diam + 4);
+
+ for(k=0; k<diam; k++){
+ for(l=0; l<diam; l++){
+ buffer[i + j + k*bw + l] = outer;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+ for(i=bw; i<bt-bw-(bw*diam); i+=bw){
+ for(j=1; j<pw-1-diam; j++){
+
+ int curr = 0;
+ int hits = 0;
+
+ for(k=0; k<diam; k++){
+ for(l=0; l<diam; l++){
+ curr += buffer[i + k*bw + (j+l)/8] >> (7-(j+l)%8) & 1;
+ }
+ }
+
+ if(!curr)
+ continue;
+
+ /*loop over rows and columns around window */
+ for(k=-1; k<diam+1; k++){
+ for(l=-1; l<diam+1; l++){
+
+ /* dont count pixels in the window */
+ if(k != -1 && k != diam && l != -1 && l != diam)
+ continue;
+
+ hits += buffer[i + k*bw + (j+l)/8] >> (7-(j+l)%8) & 1;
+
+ if(hits)
+ break;
+ }
+ }
+
+ /*no hits, overwrite with white*/
+ if(!hits){
+ for(k=0; k<diam; k++){
+ for(l=0; l<diam; l++){
+ buffer[i + k*bw + (j+l)/8] &= ~(1 << (7-(j+l)%8));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else{
+ DBG (5, "sanei_magic_despeck: unsupported format/depth\n");
+ ret = SANE_STATUS_INVAL;
+ }
+
+ DBG (10, "sanei_magic_despeck: finish\n");
+ return ret;
+}
+
+/* find likely edges of media inside image background color */
+SANE_Status
+sanei_magic_findEdges(SANE_Parameters * params, SANE_Byte * buffer,
+ int dpiX, int dpiY, int * top, int * bot, int * left, int * right)
+{
+
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int width = params->pixels_per_line;
+ int height = params->lines;
+
+ int * topBuf = NULL, * botBuf = NULL;
+ int * leftBuf = NULL, * rightBuf = NULL;
+
+ int topCount = 0, botCount = 0;
+ int leftCount = 0, rightCount = 0;
+
+ int i;
+
+ DBG (10, "sanei_magic_findEdges: start\n");
+
+ /* get buffers to find sides and bottom */
+ topBuf = sanei_magic_getTransY(params,dpiY,buffer,1);
+ if(!topBuf){
+ DBG (5, "sanei_magic_findEdges: no topBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ botBuf = sanei_magic_getTransY(params,dpiY,buffer,0);
+ if(!botBuf){
+ DBG (5, "sanei_magic_findEdges: no botBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ leftBuf = sanei_magic_getTransX(params,dpiX,buffer,1);
+ if(!leftBuf){
+ DBG (5, "sanei_magic_findEdges: no leftBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ rightBuf = sanei_magic_getTransX(params,dpiX,buffer,0);
+ if(!rightBuf){
+ DBG (5, "sanei_magic_findEdges: no rightBuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* loop thru left and right lists, look for top and bottom extremes */
+ *top = height;
+ for(i=0; i<height; i++){
+ if(rightBuf[i] > leftBuf[i]){
+ if(*top > i){
+ *top = i;
+ }
+
+ topCount++;
+ if(topCount > 3){
+ break;
+ }
+ }
+ else{
+ topCount = 0;
+ *top = height;
+ }
+ }
+
+ *bot = -1;
+ for(i=height-1; i>=0; i--){
+ if(rightBuf[i] > leftBuf[i]){
+ if(*bot < i){
+ *bot = i;
+ }
+
+ botCount++;
+ if(botCount > 3){
+ break;
+ }
+ }
+ else{
+ botCount = 0;
+ *bot = -1;
+ }
+ }
+
+ /* could not find top/bot edges */
+ if(*top > *bot){
+ DBG (5, "sanei_magic_findEdges: bad t/b edges\n");
+ ret = SANE_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ /* loop thru top and bottom lists, look for l and r extremes
+ * NOTE: We dont look above the top or below the bottom found previously.
+ * This prevents issues with adf scanners that pad the image after the
+ * paper runs out (usually with white) */
+ DBG (5, "sanei_magic_findEdges: bb0:%d tb0:%d b:%d t:%d\n",
+ botBuf[0], topBuf[0], *bot, *top);
+
+ *left = width;
+ for(i=0; i<width; i++){
+ if(botBuf[i] > topBuf[i] && (botBuf[i]-10 < *bot || topBuf[i]+10 > *top)){
+ if(*left > i){
+ *left = i;
+ }
+
+ leftCount++;
+ if(leftCount > 3){
+ break;
+ }
+ }
+ else{
+ leftCount = 0;
+ *left = width;
+ }
+ }
+
+ *right = -1;
+ for(i=width-1; i>=0; i--){
+ if(botBuf[i] > topBuf[i] && (botBuf[i]-10 < *bot || topBuf[i]+10 > *top)){
+ if(*right < i){
+ *right = i;
+ }
+
+ rightCount++;
+ if(rightCount > 3){
+ break;
+ }
+ }
+ else{
+ rightCount = 0;
+ *right = -1;
+ }
+ }
+
+ /* could not find left/right edges */
+ if(*left > *right){
+ DBG (5, "sanei_magic_findEdges: bad l/r edges\n");
+ ret = SANE_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ DBG (15, "sanei_magic_findEdges: t:%d b:%d l:%d r:%d\n",
+ *top,*bot,*left,*right);
+
+ cleanup:
+ if(topBuf)
+ free(topBuf);
+ if(botBuf)
+ free(botBuf);
+ if(leftBuf)
+ free(leftBuf);
+ if(rightBuf)
+ free(rightBuf);
+
+ DBG (10, "sanei_magic_findEdges: finish\n");
+ return ret;
+}
+
+/* crop image to given size. updates params with new dimensions */
+SANE_Status
+sanei_magic_crop(SANE_Parameters * params, SANE_Byte * buffer,
+ int top, int bot, int left, int right)
+{
+
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int bwidth = params->bytes_per_line;
+
+ int pixels = 0;
+ int bytes = 0;
+ unsigned char * line = NULL;
+ int pos = 0, i;
+
+ DBG (10, "sanei_magic_crop: start\n");
+
+ /*convert left and right to bytes, figure new byte and pixel width */
+ if(params->format == SANE_FRAME_RGB){
+ pixels = right-left;
+ bytes = pixels * 3;
+ left *= 3;
+ right *= 3;
+ }
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 8){
+ pixels = right-left;
+ bytes = right-left;
+ }
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+ left /= 8;
+ right = (right+7)/8;
+ bytes = right-left;
+ pixels = bytes * 8;
+ }
+ else{
+ DBG (5, "sanei_magic_crop: unsupported format/depth\n");
+ ret = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ DBG (15, "sanei_magic_crop: l:%d r:%d p:%d b:%d\n",left,right,pixels,bytes);
+
+ line = malloc(bytes);
+ if(!line){
+ DBG (5, "sanei_magic_crop: no line\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(i=top; i<bot; i++){
+ memcpy(line, buffer + i*bwidth + left, bytes);
+ memcpy(buffer + pos, line, bytes);
+ pos += bytes;
+ }
+
+ /* update the params struct with the new image size */
+ params->lines = bot-top;
+ params->pixels_per_line = pixels;
+ params->bytes_per_line = bytes;
+
+ cleanup:
+ if(line)
+ free(line);
+
+ DBG (10, "sanei_magic_crop: finish\n");
+ return ret;
+}
+
+/* find angle of media rotation against image background */
+SANE_Status
+sanei_magic_findSkew(SANE_Parameters * params, SANE_Byte * buffer,
+ int dpiX, int dpiY, int * centerX, int * centerY, double * finSlope)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int pwidth = params->pixels_per_line;
+ int height = params->lines;
+
+ double TSlope = 0;
+ int TXInter = 0;
+ int TYInter = 0;
+ double TSlopeHalf = 0;
+ int TOffsetHalf = 0;
+
+ double LSlope = 0;
+ int LXInter = 0;
+ int LYInter = 0;
+ double LSlopeHalf = 0;
+ int LOffsetHalf = 0;
+
+ int rotateX = 0;
+ int rotateY = 0;
+
+ int * topBuf = NULL, * botBuf = NULL;
+
+ DBG (10, "sanei_magic_findSkew: start\n");
+
+ dpiX=dpiX;
+
+ /* get buffers for edge detection */
+ topBuf = sanei_magic_getTransY(params,dpiY,buffer,1);
+ if(!topBuf){
+ DBG (5, "sanei_magic_findSkew: cant gTY\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ botBuf = sanei_magic_getTransY(params,dpiY,buffer,0);
+ if(!botBuf){
+ DBG (5, "sanei_magic_findSkew: cant gTY\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* find best top line */
+ ret = getTopEdge (pwidth, height, dpiY, topBuf,
+ &TSlope, &TXInter, &TYInter);
+ if(ret){
+ DBG(5,"sanei_magic_findSkew: gTE error: %d",ret);
+ goto cleanup;
+ }
+ DBG(15,"top: %04.04f %d %d\n",TSlope,TXInter,TYInter);
+
+ /* slope is too shallow, don't want to divide by 0 */
+ if(fabs(TSlope) < 0.0001){
+ DBG(15,"sanei_magic_findSkew: slope too shallow: %0.08f\n",TSlope);
+ ret = SANE_STATUS_UNSUPPORTED;
+ goto cleanup;
+ }
+
+ /* find best left line, perpendicular to top line */
+ LSlope = (double)-1/TSlope;
+ ret = getLeftEdge (pwidth, height, topBuf, botBuf, LSlope,
+ &LXInter, &LYInter);
+ if(ret){
+ DBG(5,"sanei_magic_findSkew: gLE error: %d",ret);
+ goto cleanup;
+ }
+ DBG(15,"sanei_magic_findSkew: left: %04.04f %d %d\n",LSlope,LXInter,LYInter);
+
+ /* find point about which to rotate */
+ TSlopeHalf = tan(atan(TSlope)/2);
+ TOffsetHalf = LYInter;
+ DBG(15,"sanei_magic_findSkew: top half: %04.04f %d\n",TSlopeHalf,TOffsetHalf);
+
+ LSlopeHalf = tan((atan(LSlope) + ((LSlope < 0)?-M_PI_2:M_PI_2))/2);
+ LOffsetHalf = - LSlopeHalf * TXInter;
+ DBG(15,"sanei_magic_findSkew: left half: %04.04f %d\n",LSlopeHalf,LOffsetHalf);
+
+ rotateX = (LOffsetHalf-TOffsetHalf) / (TSlopeHalf-LSlopeHalf);
+ rotateY = TSlopeHalf * rotateX + TOffsetHalf;
+ DBG(15,"sanei_magic_findSkew: rotate: %d %d\n",rotateX,rotateY);
+
+ *centerX = rotateX;
+ *centerY = rotateY;
+ *finSlope = TSlope;
+
+ cleanup:
+ if(topBuf)
+ free(topBuf);
+ if(botBuf)
+ free(botBuf);
+
+ DBG (10, "sanei_magic_findSkew: finish\n");
+ return ret;
+}
+
+/* function to do a simple rotation by a given slope, around
+ * a given point. The point can be outside of image to get
+ * proper edge alignment. Unused areas filled with bg color
+ * FIXME: Do in-place rotation to save memory */
+SANE_Status
+sanei_magic_rotate (SANE_Parameters * params, SANE_Byte * buffer,
+ int centerX, int centerY, double slope, int bg_color)
+{
+
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ double slopeRad = -atan(slope);
+ double slopeSin = sin(slopeRad);
+ double slopeCos = cos(slopeRad);
+
+ int pwidth = params->pixels_per_line;
+ int bwidth = params->bytes_per_line;
+ int height = params->lines;
+ int depth = 1;
+
+ unsigned char * outbuf;
+ int i, j, k;
+
+ DBG(10,"sanei_magic_rotate: start: %d %d\n",centerX,centerY);
+
+ outbuf = malloc(bwidth*height);
+ if(!outbuf){
+ DBG(15,"sanei_magic_rotate: no outbuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ if(params->format == SANE_FRAME_RGB ||
+ (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+
+ if(params->format == SANE_FRAME_RGB)
+ depth = 3;
+
+ memset(outbuf,bg_color,bwidth*height);
+
+ for (i=0; i<height; i++) {
+ int shiftY = centerY - i;
+
+ for (j=0; j<pwidth; j++) {
+ int shiftX = centerX - j;
+ int sourceX, sourceY;
+
+ sourceX = centerX - (int)(shiftX * slopeCos + shiftY * slopeSin);
+ if (sourceX < 0 || sourceX >= pwidth)
+ continue;
+
+ sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin);
+ if (sourceY < 0 || sourceY >= height)
+ continue;
+
+ for (k=0; k<depth; k++) {
+ outbuf[i*bwidth+j*depth+k]
+ = buffer[sourceY*bwidth+sourceX*depth+k];
+ }
+ }
+ }
+ }
+
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ if(bg_color)
+ bg_color = 0xff;
+
+ memset(outbuf,bg_color,bwidth*height);
+
+ for (i=0; i<height; i++) {
+ int shiftY = centerY - i;
+
+ for (j=0; j<pwidth; j++) {
+ int shiftX = centerX - j;
+ int sourceX, sourceY;
+
+ sourceX = centerX - (int)(shiftX * slopeCos + shiftY * slopeSin);
+ if (sourceX < 0 || sourceX >= pwidth)
+ continue;
+
+ sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin);
+ if (sourceY < 0 || sourceY >= height)
+ continue;
+
+ /* wipe out old bit */
+ outbuf[i*bwidth + j/8] &= ~(1 << (7-(j%8)));
+
+ /* fill in new bit */
+ outbuf[i*bwidth + j/8] |=
+ ((buffer[sourceY*bwidth + sourceX/8]
+ >> (7-(sourceX%8))) & 1) << (7-(j%8));
+ }
+ }
+ }
+ else{
+ DBG (5, "sanei_magic_rotate: unsupported format/depth\n");
+ ret = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ memcpy(buffer,outbuf,bwidth*height);
+
+ cleanup:
+
+ if(outbuf)
+ free(outbuf);
+
+ DBG(10,"sanei_magic_rotate: finish\n");
+
+ return 0;
+}
+
+SANE_Status
+sanei_magic_isBlank (SANE_Parameters * params, SANE_Byte * buffer,
+ double thresh)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ double imagesum = 0;
+ int i, j;
+
+ DBG(10,"sanei_magic_isBlank: start: %f\n",thresh);
+
+ /*convert thresh from percent (0-100) to 0-1 range*/
+ thresh /= 100;
+
+ if(params->format == SANE_FRAME_RGB ||
+ (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+
+ /* loop over all rows, find density of each */
+ for(i=0; i<params->lines; i++){
+ int rowsum = 0;
+ SANE_Byte * ptr = buffer + params->bytes_per_line*i;
+
+ /* loop over all columns, sum the 'darkness' of the pixels */
+ for(j=0; j<params->bytes_per_line; j++){
+ rowsum += 255 - ptr[j];
+ }
+
+ imagesum += (double)rowsum/params->bytes_per_line/255;
+ }
+
+ }
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ /* loop over all rows, find density of each */
+ for(i=0; i<params->lines; i++){
+ int rowsum = 0;
+ SANE_Byte * ptr = buffer + params->bytes_per_line*i;
+
+ /* loop over all columns, sum the pixels */
+ for(j=0; j<params->pixels_per_line; j++){
+ rowsum += ptr[j/8] >> (7-(j%8)) & 1;
+ }
+
+ imagesum += (double)rowsum/params->pixels_per_line;
+ }
+
+ }
+ else{
+ DBG (5, "sanei_magic_isBlank: unsupported format/depth\n");
+ ret = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ DBG (5, "sanei_magic_isBlank: sum:%f lines:%d thresh:%f density:%f\n",
+ imagesum,params->lines,thresh,imagesum/params->lines);
+
+ if(imagesum/params->lines <= thresh){
+ DBG (5, "sanei_magic_isBlank: blank!\n");
+ ret = SANE_STATUS_NO_DOCS;
+ }
+
+ cleanup:
+
+ DBG(10,"sanei_magic_isBlank: finish\n");
+
+ return ret;
+}
+
+/* Divide the image into 1/2 inch squares, skipping a 1/4 inch
+ * margin on all sides. If all squares are under the user's density,
+ * signal our caller to skip the image entirely, by returning
+ * SANE_STATUS_NO_DOCS */
+SANE_Status
+sanei_magic_isBlank2 (SANE_Parameters * params, SANE_Byte * buffer,
+ int dpiX, int dpiY, double thresh)
+{
+ int xb,yb,x,y;
+
+ /* .25 inch, rounded down to 8 pixel */
+ int xquarter = dpiX/4/8*8;
+ int yquarter = dpiY/4/8*8;
+ int xhalf = xquarter*2;
+ int yhalf = yquarter*2;
+ int blockpix = xhalf*yhalf;
+ int xblocks = (params->pixels_per_line-xhalf)/xhalf;
+ int yblocks = (params->lines-yhalf)/yhalf;
+
+ /*convert thresh from percent (0-100) to 0-1 range*/
+ thresh /= 100;
+
+ DBG (10, "sanei_magic_isBlank2: start %d %d %f %d\n",xhalf,yhalf,thresh,blockpix);
+
+ if(params->depth == 8 &&
+ (params->format == SANE_FRAME_RGB || params->format == SANE_FRAME_GRAY)
+ ){
+
+ int Bpp = params->format == SANE_FRAME_RGB ? 3 : 1;
+
+ for(yb=0; yb<yblocks; yb++){
+ for(xb=0; xb<xblocks; xb++){
+
+ /*count dark pix in this block*/
+ double blocksum = 0;
+
+ for(y=0; y<yhalf; y++){
+
+ /* skip the top and left 1/4 inch */
+ int offset = (yquarter + yb*yhalf + y) * params->bytes_per_line
+ + (xquarter + xb*xhalf) * Bpp;
+ SANE_Byte * ptr = buffer + offset;
+
+ /*count darkness of pix in this row*/
+ int rowsum = 0;
+
+ for(x=0; x<xhalf*Bpp; x++){
+ rowsum += 255 - ptr[x];
+ }
+
+ blocksum += (double)rowsum/(xhalf*Bpp)/255;
+ }
+
+ /* block was darker than thresh, keep image */
+ if(blocksum/yhalf > thresh){
+ DBG (15, "sanei_magic_isBlank2: not blank %f %d %d\n", blocksum/yhalf, yb, xb);
+ return SANE_STATUS_GOOD;
+ }
+ DBG (20, "sanei_magic_isBlank2: block blank %f %d %d\n", blocksum/yhalf, yb, xb);
+ }
+ }
+ }
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ for(yb=0; yb<yblocks; yb++){
+ for(xb=0; xb<xblocks; xb++){
+
+ /*count dark pix in this block*/
+ double blocksum = 0;
+
+ for(y=0; y<yhalf; y++){
+
+ /* skip the top and left 1/4 inch */
+ int offset = (yquarter + yb*yhalf + y) * params->bytes_per_line
+ + (xquarter + xb*xhalf) / 8;
+ SANE_Byte * ptr = buffer + offset;
+
+ /*count darkness of pix in this row*/
+ int rowsum = 0;
+
+ for(x=0; x<xhalf; x++){
+ rowsum += ptr[x/8] >> (7-(x%8)) & 1;
+ }
+
+ blocksum += (double)rowsum/xhalf;
+ }
+
+ /* block was darker than thresh, keep image */
+ if(blocksum/yhalf > thresh){
+ DBG (15, "sanei_magic_isBlank2: not blank %f %d %d\n", blocksum/yhalf, yb, xb);
+ return SANE_STATUS_GOOD;
+ }
+ DBG (20, "sanei_magic_isBlank2: block blank %f %d %d\n", blocksum/yhalf, yb, xb);
+ }
+ }
+ }
+ else{
+ DBG (5, "sanei_magic_isBlank2: unsupported format/depth\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (10, "sanei_magic_isBlank2: returning blank\n");
+ return SANE_STATUS_NO_DOCS;
+}
+
+SANE_Status
+sanei_magic_findTurn(SANE_Parameters * params, SANE_Byte * buffer,
+ int dpiX, int dpiY, int * angle)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j, k;
+ int depth = 1;
+ int htrans=0, vtrans=0;
+ int htot=0, vtot=0;
+
+ DBG(10,"sanei_magic_findTurn: start\n");
+
+ if(params->format == SANE_FRAME_RGB ||
+ (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+
+ if(params->format == SANE_FRAME_RGB)
+ depth = 3;
+
+ /* loop over some rows, count segment lengths */
+ for(i=0; i<params->lines; i+=dpiY/20){
+ SANE_Byte * ptr = buffer + params->bytes_per_line*i;
+ int color = 0;
+ int len = 0;
+ int sum = 0;
+
+ /* loop over all columns */
+ for(j=0; j<params->pixels_per_line; j++){
+ int curr = 0;
+
+ /*convert color to gray*/
+ for (k=0; k<depth; k++) {
+ curr += ptr[j*depth+k];
+ }
+ curr /= depth;
+
+ /*convert gray to binary (with hysteresis) */
+ curr = (curr < 100)?1:
+ (curr > 156)?0:color;
+
+ /*count segment length*/
+ if(curr != color || j==params->pixels_per_line-1){
+ sum += len * len/5;
+ len = 0;
+ color = curr;
+ }
+ else{
+ len++;
+ }
+ }
+
+ htot++;
+ htrans += (double)sum/params->pixels_per_line;
+ }
+
+ /* loop over some cols, count dark vs light transitions */
+ for(i=0; i<params->pixels_per_line; i+=dpiX/20){
+ SANE_Byte * ptr = buffer + i*depth;
+ int color = 0;
+ int len = 0;
+ int sum = 0;
+
+ /* loop over all rows */
+ for(j=0; j<params->lines; j++){
+ int curr = 0;
+
+ /*convert color to gray*/
+ for (k=0; k<depth; k++) {
+ curr += ptr[j*params->bytes_per_line+k];
+ }
+ curr /= depth;
+
+ /*convert gray to binary (with hysteresis) */
+ curr = (curr < 100)?1:
+ (curr > 156)?0:color;
+
+ /*count segment length*/
+ if(curr != color || j==params->lines-1){
+ sum += len * len/5;
+ len = 0;
+ color = curr;
+ }
+ else{
+ len++;
+ }
+ }
+
+ vtot++;
+ vtrans += (double)sum/params->lines;
+ }
+
+ }
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ /* loop over some rows, count segment lengths */
+ for(i=0; i<params->lines; i+=dpiY/30){
+ SANE_Byte * ptr = buffer + params->bytes_per_line*i;
+ int color = 0;
+ int len = 0;
+ int sum = 0;
+
+ /* loop over all columns */
+ for(j=0; j<params->pixels_per_line; j++){
+ int curr = ptr[j/8] >> (7-(j%8)) & 1;
+
+ /*count segment length*/
+ if(curr != color || j==params->pixels_per_line-1){
+ sum += len * len/5;
+ len = 0;
+ color = curr;
+ }
+ else{
+ len++;
+ }
+ }
+
+ htot++;
+ htrans += (double)sum/params->pixels_per_line;
+ }
+
+ /* loop over some cols, count dark vs light transitions */
+ for(i=0; i<params->pixels_per_line; i+=dpiX/30){
+ SANE_Byte * ptr = buffer;
+ int color = 0;
+ int len = 0;
+ int sum = 0;
+
+ /* loop over all rows */
+ for(j=0; j<params->lines; j++){
+ int curr = ptr[j*params->bytes_per_line + i/8] >> (7-(i%8)) & 1;
+
+ /*count segment length*/
+ if(curr != color || j==params->lines-1){
+ sum += len * len/5;
+ len = 0;
+ color = curr;
+ }
+ else{
+ len++;
+ }
+ }
+
+ vtot++;
+ vtrans += (double)sum/params->lines;
+ }
+
+ }
+ else{
+ DBG (5, "sanei_magic_findTurn: unsupported format/depth\n");
+ ret = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ DBG (10, "sanei_magic_findTurn: vtrans=%d vtot=%d vfrac=%f htrans=%d htot=%d hfrac=%f\n",
+ vtrans, vtot, (double)vtrans/vtot, htrans, htot, (double)htrans/htot
+ );
+
+ if((double)vtrans/vtot > (double)htrans/htot){
+ DBG (10, "sanei_magic_findTurn: suggest turning 90\n");
+ *angle = 90;
+ }
+
+ cleanup:
+
+ DBG(10,"sanei_magic_findTurn: finish\n");
+
+ return ret;
+}
+
+/* FIXME: Do in-place rotation to save memory */
+SANE_Status
+sanei_magic_turn(SANE_Parameters * params, SANE_Byte * buffer,
+ int angle)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int opwidth, ipwidth = params->pixels_per_line;
+ int obwidth, ibwidth = params->bytes_per_line;
+ int oheight, iheight = params->lines;
+ int depth = 1;
+
+ unsigned char * outbuf = NULL;
+ int i, j, k;
+
+ DBG(10,"sanei_magic_turn: start %d\n",angle);
+
+ if(params->format == SANE_FRAME_RGB)
+ depth = 3;
+
+ /*clean angle and convert to 0-3*/
+ angle = (angle % 360) / 90;
+
+ /*figure size of output image*/
+ switch(angle){
+ case 1:
+ case 3:
+ opwidth = iheight;
+ oheight = ipwidth;
+
+ /*gray and color, 1 or 3 bytes per pixel*/
+ if ( params->format == SANE_FRAME_RGB
+ || (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+ obwidth = opwidth*depth;
+ }
+
+ /*clamp binary to byte width. must be <= input image*/
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+ obwidth = opwidth/8;
+ opwidth = obwidth*8;
+ }
+
+ else{
+ DBG(10,"sanei_magic_turn: bad params\n");
+ ret = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ break;
+
+ case 2:
+ opwidth = ipwidth;
+ obwidth = ibwidth;
+ oheight = iheight;
+ break;
+
+ default:
+ DBG(10,"sanei_magic_turn: no turn\n");
+ goto cleanup;
+ }
+
+ /*get output image buffer*/
+ outbuf = malloc(obwidth*oheight);
+ if(!outbuf){
+ DBG(15,"sanei_magic_turn: no outbuf\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /*turn color & gray image*/
+ if(params->format == SANE_FRAME_RGB ||
+ (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+
+ switch (angle) {
+
+ /*rotate 90 clockwise*/
+ case 1:
+ for (i=0; i<oheight; i++) {
+ for (j=0; j<opwidth; j++) {
+ for (k=0; k<depth; k++) {
+ outbuf[i*obwidth + j*depth + k]
+ = buffer[(iheight-j-1)*ibwidth + i*depth + k];
+ }
+ }
+ }
+ break;
+
+ /*rotate 180 clockwise*/
+ case 2:
+ for (i=0; i<oheight; i++) {
+ for (j=0; j<opwidth; j++) {
+ for (k=0; k<depth; k++) {
+ outbuf[i*obwidth + j*depth + k]
+ = buffer[(iheight-i-1)*ibwidth + (ipwidth-j-1)*depth + k];
+ }
+ }
+ }
+ break;
+
+ /*rotate 270 clockwise*/
+ case 3:
+ for (i=0; i<oheight; i++) {
+ for (j=0; j<opwidth; j++) {
+ for (k=0; k<depth; k++) {
+ outbuf[i*obwidth + j*depth + k]
+ = buffer[j*ibwidth + (ipwidth-i-1)*depth + k];
+ }
+ }
+ }
+ break;
+ } /*end switch*/
+ }
+
+ /*turn binary image*/
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ switch (angle) {
+
+ /*rotate 90 clockwise*/
+ case 1:
+ for (i=0; i<oheight; i++) {
+ for (j=0; j<opwidth; j++) {
+ unsigned char curr
+ = buffer[(iheight-j-1)*ibwidth + i/8] >> (7-(i%8)) & 1;
+
+ unsigned char mask = 1 << (7-(j%8));
+
+ if(curr){
+ outbuf[i*obwidth + j/8] |= mask;
+ }
+ else{
+ outbuf[i*obwidth + j/8] &= (~mask);
+ }
+
+ }
+ }
+ break;
+
+ /*rotate 180 clockwise*/
+ case 2:
+ for (i=0; i<oheight; i++) {
+ for (j=0; j<opwidth; j++) {
+ unsigned char curr
+ = buffer[(iheight-i-1)*ibwidth + (ipwidth-j-1)/8] >> (j%8) & 1;
+
+ unsigned char mask = 1 << (7-(j%8));
+
+ if(curr){
+ outbuf[i*obwidth + j/8] |= mask;
+ }
+ else{
+ outbuf[i*obwidth + j/8] &= (~mask);
+ }
+
+ }
+ }
+ break;
+
+ /*rotate 270 clockwise*/
+ case 3:
+ for (i=0; i<oheight; i++) {
+ for (j=0; j<opwidth; j++) {
+ unsigned char curr
+ = buffer[j*ibwidth + (ipwidth-i-1)/8] >> (i%8) & 1;
+
+ unsigned char mask = 1 << (7-(j%8));
+
+ if(curr){
+ outbuf[i*obwidth + j/8] |= mask;
+ }
+ else{
+ outbuf[i*obwidth + j/8] &= (~mask);
+ }
+
+ }
+ }
+ break;
+ } /*end switch*/
+ }
+
+ else{
+ DBG (5, "sanei_magic_turn: unsupported format/depth\n");
+ ret = SANE_STATUS_INVAL;
+ goto cleanup;
+ }
+
+ /*copy output back into input buffer*/
+ memcpy(buffer,outbuf,obwidth*oheight);
+
+ /*update input params*/
+ params->pixels_per_line = opwidth;
+ params->bytes_per_line = obwidth;
+ params->lines = oheight;
+
+ cleanup:
+
+ if(outbuf)
+ free(outbuf);
+
+ DBG(10,"sanei_magic_turn: finish\n");
+
+ return ret;
+}
+
+/* Utility functions, not used outside this file */
+
+/* Repeatedly call getLine to find the best range of slope and offset.
+ * Shift the ranges thru 4 different positions to avoid splitting data
+ * across multiple bins (false positive). Home-in on the most likely upper
+ * line of the paper inside the image. Return the 'best' edge. */
+static SANE_Status
+getTopEdge(int width, int height, int resolution,
+ int * buff, double * finSlope, int * finXInter, int * finYInter)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ int slopes = 31;
+ int offsets = 31;
+ double maxSlope = 1;
+ double minSlope = -1;
+ int maxOffset = resolution;
+ int minOffset = -resolution;
+
+ double topSlope = 0;
+ int topOffset = 0;
+ int topDensity = 0;
+
+ int i,j;
+ int pass = 0;
+
+ DBG(10,"getTopEdge: start\n");
+
+ while(pass++ < 7){
+ double sStep = (maxSlope-minSlope)/slopes;
+ int oStep = (maxOffset-minOffset)/offsets;
+
+ double slope = 0;
+ int offset = 0;
+ int density = 0;
+ int go = 0;
+
+ topSlope = 0;
+ topOffset = 0;
+ topDensity = 0;
+
+ /* find lines 4 times with slightly moved params,
+ * to bypass binning errors, highest density wins */
+ for(i=0;i<2;i++){
+ double sStep2 = sStep*i/2;
+ for(j=0;j<2;j++){
+ int oStep2 = oStep*j/2;
+ ret = getLine(height,width,buff,slopes,minSlope+sStep2,maxSlope+sStep2,offsets,minOffset+oStep2,maxOffset+oStep2,&slope,&offset,&density);
+ if(ret){
+ DBG(5,"getTopEdge: getLine error %d\n",ret);
+ return ret;
+ }
+ DBG(15,"getTopEdge: %d %d %+0.4f %d %d\n",i,j,slope,offset,density);
+
+ if(density > topDensity){
+ topSlope = slope;
+ topOffset = offset;
+ topDensity = density;
+ }
+ }
+ }
+
+ DBG(15,"getTopEdge: ok %+0.4f %d %d\n",topSlope,topOffset,topDensity);
+
+ /* did not find anything promising on first pass,
+ * give up instead of fixating on some small, pointless feature */
+ if(pass == 1 && topDensity < width/5){
+ DBG(5,"getTopEdge: density too small %d %d\n",topDensity,width);
+ topOffset = 0;
+ topSlope = 0;
+ break;
+ }
+
+ /* if slope can zoom in some more, do so. */
+ if(sStep >= 0.0001){
+ minSlope = topSlope - sStep;
+ maxSlope = topSlope + sStep;
+ go = 1;
+ }
+
+ /* if offset can zoom in some more, do so. */
+ if(oStep){
+ minOffset = topOffset - oStep;
+ maxOffset = topOffset + oStep;
+ go = 1;
+ }
+
+ /* cannot zoom in more, bail out */
+ if(!go){
+ break;
+ }
+
+ DBG(15,"getTopEdge: zoom: %+0.4f %+0.4f %d %d\n",
+ minSlope,maxSlope,minOffset,maxOffset);
+ }
+
+ /* topOffset is in the center of the image,
+ * convert to x and y intercept */
+ if(topSlope != 0){
+ *finYInter = topOffset - topSlope * width/2;
+ *finXInter = *finYInter / -topSlope;
+ *finSlope = topSlope;
+ }
+ else{
+ *finYInter = 0;
+ *finXInter = 0;
+ *finSlope = 0;
+ }
+
+ DBG(10,"getTopEdge: finish\n");
+
+ return 0;
+}
+
+/* Loop thru a transition array, and use a simplified Hough transform
+ * to divide likely edges into a 2-d array of bins. Then weight each
+ * bin based on its angle and offset. Return the 'best' bin. */
+static SANE_Status
+getLine (int height, int width, int * buff,
+ int slopes, double minSlope, double maxSlope,
+ int offsets, int minOffset, int maxOffset,
+ double * finSlope, int * finOffset, int * finDensity)
+{
+ SANE_Status ret = 0;
+
+ int ** lines = NULL;
+ int i, j;
+ int rise, run;
+ double slope;
+ int offset;
+ int sIndex, oIndex;
+ int hWidth = width/2;
+
+ double * slopeCenter = NULL;
+ int * slopeScale = NULL;
+ double * offsetCenter = NULL;
+ int * offsetScale = NULL;
+
+ int maxDensity = 1;
+ double absMaxSlope = fabs(maxSlope);
+ double absMinSlope = fabs(minSlope);
+ int absMaxOffset = abs(maxOffset);
+ int absMinOffset = abs(minOffset);
+
+ DBG(10,"getLine: start %+0.4f %+0.4f %d %d\n",
+ minSlope,maxSlope,minOffset,maxOffset);
+
+ /*silence compiler*/
+ height = height;
+
+ if(absMaxSlope < absMinSlope)
+ absMaxSlope = absMinSlope;
+
+ if(absMaxOffset < absMinOffset)
+ absMaxOffset = absMinOffset;
+
+ /* build an array of pretty-print values for slope */
+ slopeCenter = calloc(slopes,sizeof(double));
+ if(!slopeCenter){
+ DBG(5,"getLine: cant load slopeCenter\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* build an array of scaling factors for slope */
+ slopeScale = calloc(slopes,sizeof(int));
+ if(!slopeScale){
+ DBG(5,"getLine: cant load slopeScale\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(j=0;j<slopes;j++){
+
+ /* find central value of this 'bucket' */
+ slopeCenter[j] = (
+ (double)j*(maxSlope-minSlope)/slopes+minSlope
+ + (double)(j+1)*(maxSlope-minSlope)/slopes+minSlope
+ )/2;
+
+ /* scale value from the requested range into an inverted 100-1 range
+ * input close to 0 makes output close to 100 */
+ slopeScale[j] = 101 - fabs(slopeCenter[j])*100/absMaxSlope;
+ }
+
+ /* build an array of pretty-print values for offset */
+ offsetCenter = calloc(offsets,sizeof(double));
+ if(!offsetCenter){
+ DBG(5,"getLine: cant load offsetCenter\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ /* build an array of scaling factors for offset */
+ offsetScale = calloc(offsets,sizeof(int));
+ if(!offsetScale){
+ DBG(5,"getLine: cant load offsetScale\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(j=0;j<offsets;j++){
+
+ /* find central value of this 'bucket'*/
+ offsetCenter[j] = (
+ (double)j/offsets*(maxOffset-minOffset)+minOffset
+ + (double)(j+1)/offsets*(maxOffset-minOffset)+minOffset
+ )/2;
+
+ /* scale value from the requested range into an inverted 100-1 range
+ * input close to 0 makes output close to 100 */
+ offsetScale[j] = 101 - fabs(offsetCenter[j])*100/absMaxOffset;
+ }
+
+ /* build 2-d array of 'density', divided into slope and offset ranges */
+ lines = calloc(slopes, sizeof(int *));
+ if(!lines){
+ DBG(5,"getLine: cant load lines\n");
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+
+ for(i=0;i<slopes;i++){
+ if(!(lines[i] = calloc(offsets, sizeof(int)))){
+ DBG(5,"getLine: cant load lines %d\n",i);
+ ret = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+
+ for(i=0;i<width;i++){
+ for(j=i+1;j<width && j<i+width/3;j++){
+
+ /*FIXME: check for invalid (min/max) values?*/
+ rise = buff[j] - buff[i];
+ run = j-i;
+
+ slope = (double)rise/run;
+ if(slope >= maxSlope || slope < minSlope)
+ continue;
+
+ /* offset in center of width, not y intercept! */
+ offset = slope * hWidth + buff[i] - slope * i;
+ if(offset >= maxOffset || offset < minOffset)
+ continue;
+
+ sIndex = (slope - minSlope) * slopes/(maxSlope-minSlope);
+ if(sIndex >= slopes)
+ continue;
+
+ oIndex = (offset - minOffset) * offsets/(maxOffset-minOffset);
+ if(oIndex >= offsets)
+ continue;
+
+ lines[sIndex][oIndex]++;
+ }
+ }
+
+ /* go thru array, and find most dense line (highest number) */
+ for(i=0;i<slopes;i++){
+ for(j=0;j<offsets;j++){
+ if(lines[i][j] > maxDensity)
+ maxDensity = lines[i][j];
+ }
+ }
+
+ DBG(15,"getLine: maxDensity %d\n",maxDensity);
+
+ *finSlope = 0;
+ *finOffset = 0;
+ *finDensity = 0;
+
+ /* go thru array, and scale densities to % of maximum, plus adjust for
+ * prefered (smaller absolute value) slope and offset */
+ for(i=0;i<slopes;i++){
+ for(j=0;j<offsets;j++){
+ lines[i][j] = (float)lines[i][j] / maxDensity * slopeScale[i] * offsetScale[j];
+ if(lines[i][j] > *finDensity){
+ *finDensity = lines[i][j];
+ *finSlope = slopeCenter[i];
+ *finOffset = offsetCenter[j];
+ }
+ }
+ }
+
+ if(0){
+ fprintf(stderr,"offsetCenter: ");
+ for(j=0;j<offsets;j++){
+ fprintf(stderr," %+04.0f",offsetCenter[j]);
+ }
+ fprintf(stderr,"\n");
+
+ fprintf(stderr,"offsetScale: ");
+ for(j=0;j<offsets;j++){
+ fprintf(stderr," %04d",offsetScale[j]);
+ }
+ fprintf(stderr,"\n");
+
+ for(i=0;i<slopes;i++){
+ fprintf(stderr,"slope: %02d %+02.2f %03d:",i,slopeCenter[i],slopeScale[i]);
+ for(j=0;j<offsets;j++){
+ fprintf(stderr,"% 5d",lines[i][j]);
+ }
+ fprintf(stderr,"\n");
+ }
+ }
+
+ /* dont forget to cleanup */
+ cleanup:
+ for(i=0;i<slopes;i++){
+ if(lines[i])
+ free(lines[i]);
+ }
+ if(lines)
+ free(lines);
+ if(slopeCenter)
+ free(slopeCenter);
+ if(slopeScale)
+ free(slopeScale);
+ if(offsetCenter)
+ free(offsetCenter);
+ if(offsetScale)
+ free(offsetScale);
+
+ DBG(10,"getLine: finish\n");
+
+ return ret;
+}
+
+/* find the left side of paper by moving a line
+ * perpendicular to top slope across the image
+ * the 'left-most' point on the paper is the
+ * one with the smallest X intercept
+ * return x and y intercepts */
+static SANE_Status
+getLeftEdge (int width, int height, int * top, int * bot,
+ double slope, int * finXInter, int * finYInter)
+{
+
+ int i;
+ int topXInter, topYInter;
+ int botXInter, botYInter;
+ int leftCount;
+
+ DBG(10,"getEdgeSlope: start\n");
+
+ topXInter = width;
+ topYInter = 0;
+ leftCount = 0;
+
+ for(i=0;i<width;i++){
+
+ if(top[i] < height){
+ int tyi = top[i] - (slope * i);
+ int txi = tyi/-slope;
+
+ if(topXInter > txi){
+ topXInter = txi;
+ topYInter = tyi;
+ }
+
+ leftCount++;
+ if(leftCount > 5){
+ break;
+ }
+ }
+ else{
+ topXInter = width;
+ topYInter = 0;
+ leftCount = 0;
+ }
+ }
+
+ botXInter = width;
+ botYInter = 0;
+ leftCount = 0;
+
+ for(i=0;i<width;i++){
+
+ if(bot[i] > -1){
+
+ int byi = bot[i] - (slope * i);
+ int bxi = byi/-slope;
+
+ if(botXInter > bxi){
+ botXInter = bxi;
+ botYInter = byi;
+ }
+
+ leftCount++;
+ if(leftCount > 5){
+ break;
+ }
+ }
+ else{
+ botXInter = width;
+ botYInter = 0;
+ leftCount = 0;
+ }
+ }
+
+ if(botXInter < topXInter){
+ *finXInter = botXInter;
+ *finYInter = botYInter;
+ }
+ else{
+ *finXInter = topXInter;
+ *finYInter = topYInter;
+ }
+
+ DBG(10,"getEdgeSlope: finish\n");
+
+ return 0;
+}
+
+/* Loop thru the image and look for first color change in each column.
+ * Return a malloc'd array. Caller is responsible for freeing. */
+int *
+sanei_magic_getTransY (
+ SANE_Parameters * params, int dpi, SANE_Byte * buffer, int top)
+{
+ int * buff;
+
+ int i, j, k;
+ int winLen = 9;
+
+ int width = params->pixels_per_line;
+ int height = params->lines;
+ int depth = 1;
+
+ /* defaults for bottom-up */
+ int firstLine = height-1;
+ int lastLine = -1;
+ int direction = -1;
+
+ DBG (10, "sanei_magic_getTransY: start\n");
+
+ /* override for top-down */
+ if(top){
+ firstLine = 0;
+ lastLine = height;
+ direction = 1;
+ }
+
+ /* build output and preload with impossible value */
+ buff = calloc(width,sizeof(int));
+ if(!buff){
+ DBG (5, "sanei_magic_getTransY: no buff\n");
+ return NULL;
+ }
+ for(i=0; i<width; i++)
+ buff[i] = lastLine;
+
+ /* load the buff array with y value for first color change from edge
+ * gray/color uses a different algo from binary/halftone */
+ if(params->format == SANE_FRAME_RGB ||
+ (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+
+ if(params->format == SANE_FRAME_RGB)
+ depth = 3;
+
+ /* loop over all columns, find first transition */
+ for(i=0; i<width; i++){
+
+ int near = 0;
+ int far = 0;
+
+ /* load the near and far windows with repeated copy of first pixel */
+ for(k=0; k<depth; k++){
+ near += buffer[(firstLine*width+i) * depth + k];
+ }
+ near *= winLen;
+ far = near;
+
+ /* move windows, check delta */
+ for(j=firstLine+direction; j!=lastLine; j+=direction){
+
+ int farLine = j-winLen*2*direction;
+ int nearLine = j-winLen*direction;
+
+ if(farLine < 0 || farLine >= height){
+ farLine = firstLine;
+ }
+ if(nearLine < 0 || nearLine >= height){
+ nearLine = firstLine;
+ }
+
+ for(k=0; k<depth; k++){
+ far -= buffer[(farLine*width+i)*depth+k];
+ far += buffer[(nearLine*width+i)*depth+k];
+
+ near -= buffer[(nearLine*width+i)*depth+k];
+ near += buffer[(j*width+i)*depth+k];
+ }
+
+ /* significant transition */
+ if(abs(near - far) > 50*winLen*depth - near*40/255){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ }
+
+ else if(params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ int near = 0;
+
+ for(i=0; i<width; i++){
+
+ /* load the near window with first pixel */
+ near = buffer[(firstLine*width+i)/8] >> (7-(i%8)) & 1;
+
+ /* move */
+ for(j=firstLine+direction; j!=lastLine; j+=direction){
+ if((buffer[(j*width+i)/8] >> (7-(i%8)) & 1) != near){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ }
+
+ /* some other format? */
+ else{
+ DBG (5, "sanei_magic_getTransY: unsupported format/depth\n");
+ free(buff);
+ return NULL;
+ }
+
+ /* ignore transitions with few neighbors within .5 inch */
+ for(i=0;i<width-7;i++){
+ int sum = 0;
+ for(j=1;j<=7;j++){
+ if(abs(buff[i+j] - buff[i]) < dpi/2)
+ sum++;
+ }
+ if(sum < 2)
+ buff[i] = lastLine;
+ }
+
+ DBG (10, "sanei_magic_getTransY: finish\n");
+
+ return buff;
+}
+
+/* Loop thru the image height and look for first color change in each row.
+ * Return a malloc'd array. Caller is responsible for freeing. */
+int *
+sanei_magic_getTransX (
+ SANE_Parameters * params, int dpi, SANE_Byte * buffer, int left)
+{
+ int * buff;
+
+ int i, j, k;
+ int winLen = 9;
+
+ int bwidth = params->bytes_per_line;
+ int width = params->pixels_per_line;
+ int height = params->lines;
+ int depth = 1;
+
+ /* defaults for right-first */
+ int firstCol = width-1;
+ int lastCol = -1;
+ int direction = -1;
+
+ DBG (10, "sanei_magic_getTransX: start\n");
+
+ /* override for left-first*/
+ if(left){
+ firstCol = 0;
+ lastCol = width;
+ direction = 1;
+ }
+
+ /* build output and preload with impossible value */
+ buff = calloc(height,sizeof(int));
+ if(!buff){
+ DBG (5, "sanei_magic_getTransX: no buff\n");
+ return NULL;
+ }
+ for(i=0; i<height; i++)
+ buff[i] = lastCol;
+
+ /* load the buff array with x value for first color change from edge
+ * gray/color uses a different algo from binary/halftone */
+ if(params->format == SANE_FRAME_RGB ||
+ (params->format == SANE_FRAME_GRAY && params->depth == 8)
+ ){
+
+ if(params->format == SANE_FRAME_RGB)
+ depth = 3;
+
+ /* loop over all columns, find first transition */
+ for(i=0; i<height; i++){
+
+ int near = 0;
+ int far = 0;
+
+ /* load the near and far windows with repeated copy of first pixel */
+ for(k=0; k<depth; k++){
+ near += buffer[i*bwidth + k];
+ }
+ near *= winLen;
+ far = near;
+
+ /* move windows, check delta */
+ for(j=firstCol+direction; j!=lastCol; j+=direction){
+
+ int farCol = j-winLen*2*direction;
+ int nearCol = j-winLen*direction;
+
+ if(farCol < 0 || farCol >= width){
+ farCol = firstCol;
+ }
+ if(nearCol < 0 || nearCol >= width){
+ nearCol = firstCol;
+ }
+
+ for(k=0; k<depth; k++){
+ far -= buffer[i*bwidth + farCol*depth + k];
+ far += buffer[i*bwidth + nearCol*depth + k];
+
+ near -= buffer[i*bwidth + nearCol*depth + k];
+ near += buffer[i*bwidth + j*depth + k];
+ }
+
+ if(abs(near - far) > 50*winLen*depth - near*40/255){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ }
+
+ else if (params->format == SANE_FRAME_GRAY && params->depth == 1){
+
+ int near = 0;
+
+ for(i=0; i<height; i++){
+
+ /* load the near window with first pixel */
+ near = buffer[i*bwidth + firstCol/8] >> (7-(firstCol%8)) & 1;
+
+ /* move */
+ for(j=firstCol+direction; j!=lastCol; j+=direction){
+ if((buffer[i*bwidth + j/8] >> (7-(j%8)) & 1) != near){
+ buff[i] = j;
+ break;
+ }
+ }
+ }
+ }
+
+ /* some other format? */
+ else{
+ DBG (5, "sanei_magic_getTransX: unsupported format/depth\n");
+ free(buff);
+ return NULL;
+ }
+
+ /* ignore transitions with few neighbors within .5 inch */
+ for(i=0;i<height-7;i++){
+ int sum = 0;
+ for(j=1;j<=7;j++){
+ if(abs(buff[i+j] - buff[i]) < dpi/2)
+ sum++;
+ }
+ if(sum < 2)
+ buff[i] = lastCol;
+ }
+
+ DBG (10, "sanei_magic_getTransX: finish\n");
+
+ return buff;
+}
+
diff --git a/sanei/sanei_net.c b/sanei/sanei_net.c
new file mode 100644
index 0000000..b46da70
--- /dev/null
+++ b/sanei/sanei_net.c
@@ -0,0 +1,186 @@
+/* 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_net.h"
+
+void
+sanei_w_init_req (Wire *w, SANE_Init_Req *req)
+{
+ sanei_w_word (w, &req->version_code);
+ sanei_w_string (w, &req->username);
+}
+
+void
+sanei_w_init_reply (Wire *w, SANE_Init_Reply *reply)
+{
+ sanei_w_status (w, &reply->status);
+ sanei_w_word (w, &reply->version_code);
+}
+
+void
+sanei_w_get_devices_reply (Wire *w, SANE_Get_Devices_Reply *reply)
+{
+ SANE_Word len;
+
+ if (w->direction != WIRE_DECODE)
+ {
+ if (reply->device_list)
+ {
+ for (len = 0; reply->device_list[len]; ++len);
+ ++len;
+ }
+ else
+ len = 0;
+ }
+ sanei_w_status (w, &reply->status);
+ sanei_w_array (w, &len, (void *) &reply->device_list,
+ (WireCodecFunc) sanei_w_device_ptr,
+ sizeof (reply->device_list[0]));
+}
+
+void
+sanei_w_open_reply (Wire *w, SANE_Open_Reply *reply)
+{
+ sanei_w_status (w, &reply->status);
+ sanei_w_word (w, &reply->handle);
+ sanei_w_string (w, &reply->resource_to_authorize);
+}
+
+static void
+w_option_value (Wire *w, SANE_Word type, SANE_Word size, void **value)
+{
+ SANE_Word len, element_size;
+ WireCodecFunc w_value;
+
+ switch (type)
+ {
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ w_value = (WireCodecFunc) sanei_w_word;
+ element_size = sizeof (SANE_Word);
+ len = size / element_size;
+ break;
+
+ case SANE_TYPE_STRING:
+ w_value = (WireCodecFunc) sanei_w_char;
+ element_size = sizeof (SANE_Char);
+ len = size;
+ break;
+
+ case SANE_TYPE_BUTTON:
+ case SANE_TYPE_GROUP:
+ w_value = (WireCodecFunc) sanei_w_void;
+ len = 0;
+ element_size = 0;
+ break;
+
+ default:
+ w->status = EINVAL;
+ return;
+ }
+ sanei_w_array (w, &len, value, w_value, element_size);
+}
+
+void
+sanei_w_option_descriptor_array (Wire *w, SANE_Option_Descriptor_Array *a)
+{
+ sanei_w_array (w, &a->num_options, (void **) &a->desc,
+ (WireCodecFunc) sanei_w_option_descriptor_ptr,
+ sizeof (a->desc[0]));
+}
+
+void
+sanei_w_control_option_req (Wire *w, SANE_Control_Option_Req *req)
+{
+ sanei_w_word (w, &req->handle);
+ sanei_w_word (w, &req->option);
+ sanei_w_word (w, &req->action);
+
+ /* Up to and including version 2, we incorrectly attempted to encode
+ the option value even the action was SANE_ACTION_SET_AUTO. */
+ if (w->version < 3 || req->action != SANE_ACTION_SET_AUTO)
+ {
+ sanei_w_word (w, &req->value_type);
+ sanei_w_word (w, &req->value_size);
+ w_option_value (w, req->value_type, req->value_size, &req->value);
+ }
+}
+
+void
+sanei_w_control_option_reply (Wire *w, SANE_Control_Option_Reply *reply)
+{
+ sanei_w_status (w, &reply->status);
+ sanei_w_word (w, &reply->info);
+ sanei_w_word (w, &reply->value_type);
+ sanei_w_word (w, &reply->value_size);
+ w_option_value (w, reply->value_type, reply->value_size, &reply->value);
+ sanei_w_string (w, &reply->resource_to_authorize);
+}
+
+void
+sanei_w_get_parameters_reply (Wire *w, SANE_Get_Parameters_Reply *reply)
+{
+ sanei_w_status (w, &reply->status);
+ sanei_w_parameters (w, &reply->params);
+}
+
+void
+sanei_w_start_reply (Wire *w, SANE_Start_Reply *reply)
+{
+ sanei_w_status (w, &reply->status);
+ sanei_w_word (w, &reply->port);
+ sanei_w_word (w, &reply->byte_order);
+ sanei_w_string (w, &reply->resource_to_authorize);
+}
+
+void
+sanei_w_authorization_req (Wire *w, SANE_Authorization_Req *req)
+{
+ sanei_w_string (w, &req->resource);
+ sanei_w_string (w, &req->username);
+ sanei_w_string (w, &req->password);
+}
diff --git a/sanei/sanei_pa4s2.c b/sanei/sanei_pa4s2.c
new file mode 100644
index 0000000..8fe4260
--- /dev/null
+++ b/sanei/sanei_pa4s2.c
@@ -0,0 +1,2103 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net>
+ Copyright (C) 2003 James Perry (scsi_pp functions)
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements an interface for the Mustek PP chipset A4S2 */
+
+/* debug levels:
+ 0 - nothing
+ 1 - errors
+ 2 - warnings
+ 3 - things nice to know
+ 4 - code flow
+ 5 - detailed flow
+ 6 - everything
+
+ These debug levels can be set using the environment variable
+ SANE_DEBUG_SANEI_PA4S2 */
+
+#include "../include/sane/config.h"
+
+#define BACKEND_NAME sanei_pa4s2
+#include "../include/sane/sanei_backend.h" /* pick up compatibility defs */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined(HAVE_LIBIEEE1284)
+
+# include <ieee1284.h>
+
+#elif defined(ENABLE_PARPORT_DIRECTIO)
+
+# if defined(HAVE_SYS_IO_H)
+# if defined (__ICC) && __ICC >= 700
+# define __GNUC__ 2
+# endif
+# include <sys/io.h>
+# if defined (__ICC) && __ICC >= 700
+# undef __GNUC__
+# elif defined(__ICC) && defined(HAVE_ASM_IO_H)
+# include <asm/io.h>
+# endif
+# elif defined(HAVE_ASM_IO_H)
+# include <asm/io.h> /* ugly, but backwards compatible */
+# elif defined(HAVE_SYS_HW_H)
+# include <sys/hw.h>
+# elif defined(__i386__) && ( defined (__GNUC__) || defined (__ICC) )
+
+static __inline__ void
+outb (u_char value, u_long port)
+{
+ __asm__ __volatile__ ("outb %0,%1"::"a" (value), "d" ((u_short) port));
+}
+
+static __inline__ u_char
+inb (u_long port)
+{
+ u_char value;
+
+ __asm__ __volatile__ ("inb %1,%0":"=a" (value):"d" ((u_short) port));
+ return value;
+}
+
+# else
+# define IO_SUPPORT_MISSING
+# endif
+
+#else
+
+# define IO_SUPPORT_MISSING
+
+#endif /* HAVE_LIBIEEE1284 */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_pa4s2.h"
+
+
+#ifdef NDEBUG
+#define DBG_INIT() /* basically, this is already done in sanei_debug.h... */
+
+#define TEST_DBG_INIT()
+
+#else /* !NDEBUG */
+
+static int sanei_pa4s2_dbg_init_called = SANE_FALSE;
+
+#if (!defined __GNUC__ || __GNUC__ < 2 || \
+ __GNUC_MINOR__ < (defined __cplusplus ? 6 : 4))
+
+#define TEST_DBG_INIT() if (sanei_pa4s2_dbg_init_called == SANE_FALSE) \
+ { \
+ DBG_INIT(); \
+ DBG(6, "sanei_pa4s2: interface called for" \
+ " the first time\n"); \
+ sanei_pa4s2_dbg_init_called = SANE_TRUE; \
+ }
+#else
+
+#define TEST_DBG_INIT() if (sanei_pa4s2_dbg_init_called == SANE_FALSE) \
+ { \
+ DBG_INIT(); \
+ DBG(6, "%s: interface called for" \
+ " the first time\n", __PRETTY_FUNCTION__); \
+ sanei_pa4s2_dbg_init_called = SANE_TRUE; \
+ }
+
+#endif
+
+#endif /* NDEBUG */
+
+#if defined(STDC_HEADERS)
+# include <errno.h>
+# include <stdio.h>
+# include <stdlib.h>
+#endif
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#elif defined(HAVE_STRINGS_H)
+# include <strings.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+
+#include "../include/sane/saneopts.h"
+
+
+#if (defined (HAVE_IOPERM) || defined (HAVE_LIBIEEE1284)) && !defined (IO_SUPPORT_MISSING)
+
+#if defined(STDC_HEADERS)
+# include <errno.h>
+# include <stdio.h>
+# include <stdlib.h>
+#endif
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#elif defined(HAVE_STRINGS_H)
+# include <strings.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+
+#include "../include/sane/saneopts.h"
+
+#define PA4S2_MODE_NIB 0
+#define PA4S2_MODE_UNI 1
+#define PA4S2_MODE_EPP 2
+
+#define PA4S2_ASIC_ID_1013 0xA8
+#define PA4S2_ASIC_ID_1015 0xA5
+#define PA4S2_ASIC_ID_1505 0xA2
+
+
+typedef struct
+ {
+#ifndef HAVE_LIBIEEE1284
+ const char name[6];
+ u_long base; /* i/o base address */
+#endif
+ u_int in_use; /* port in use? */
+ u_int enabled; /* port enabled? */
+ u_int mode; /* protocoll */
+ u_char prelock[3]; /* state of port */
+#ifdef HAVE_LIBIEEE1284
+ int caps;
+#endif
+ }
+PortRec, *Port;
+
+#if defined (HAVE_LIBIEEE1284)
+
+static struct parport_list pplist;
+static PortRec *port;
+
+#else
+
+static PortRec port[] =
+{
+ {"0x378", 0x378, SANE_FALSE, SANE_FALSE, PA4S2_MODE_NIB,
+ {0, 0, 0}},
+ {"0x278", 0x278, SANE_FALSE, SANE_FALSE, PA4S2_MODE_NIB,
+ {0, 0, 0}},
+ {"0x3BC", 0x3BC, SANE_FALSE, SANE_FALSE, PA4S2_MODE_NIB,
+ {0, 0, 0}}
+};
+
+#endif
+
+static u_int sanei_pa4s2_interface_options = SANEI_PA4S2_OPT_DEFAULT;
+
+extern int setuid (uid_t); /* should also be in unistd.h */
+
+static int pa4s2_open (const char *dev, SANE_Status * status);
+static void pa4s2_readbegin_epp (int fd, u_char reg);
+static u_char pa4s2_readbyte_epp (int fd);
+static void pa4s2_readend_epp (int fd);
+static void pa4s2_readbegin_uni (int fd, u_char reg);
+static u_char pa4s2_readbyte_uni (int fd);
+static void pa4s2_readend_uni (int fd);
+static void pa4s2_readbegin_nib (int fd, u_char reg);
+static u_char pa4s2_readbyte_nib (int fd);
+static void pa4s2_readend_nib (int fd);
+static void pa4s2_writebyte_any (int fd, u_char reg, u_char val);
+static int pa4s2_enable (int fd, u_char * prelock);
+static int pa4s2_disable (int fd, u_char * prelock);
+static int pa4s2_close (int fd, SANE_Status * status);
+
+#if defined (HAVE_LIBIEEE1284)
+
+static const char * pa4s2_libieee1284_errorstr(int error)
+{
+
+ switch (error)
+ {
+
+ case E1284_OK:
+ return "Everything went fine";
+
+ case E1284_NOTIMPL:
+ return "Not implemented in libieee1284";
+
+ case E1284_NOTAVAIL:
+ return "Not available on this system";
+
+ case E1284_TIMEDOUT:
+ return "Operation timed out";
+
+ case E1284_REJECTED:
+ return "IEEE 1284 negotiation rejected";
+
+ case E1284_NEGFAILED:
+ return "Negotiation went wrong";
+
+ case E1284_NOMEM:
+ return "No memory left";
+
+ case E1284_INIT:
+ return "Error initializing port";
+
+ case E1284_SYS:
+ return "Error interfacing system";
+
+ case E1284_NOID:
+ return "No IEEE 1284 ID available";
+
+ case E1284_INVALIDPORT:
+ return "Invalid port";
+
+ default:
+ return "Unknown error";
+
+ }
+}
+
+#endif
+
+static int
+pa4s2_init (SANE_Status *status)
+{
+ static int first_time = SANE_TRUE;
+#if defined (HAVE_LIBIEEE1284)
+ int result, n;
+#endif
+
+ DBG (6, "pa4s2_init: static int first_time = %u\n", first_time);
+
+ if (first_time == SANE_FALSE)
+ {
+ DBG (5, "pa4s2_init: sanei already initalized\n");
+ status = SANE_STATUS_GOOD;
+ return 0;
+ }
+
+ DBG (5, "pa4s2_init: called for the first time\n");
+
+ first_time = SANE_FALSE;
+
+#if defined (HAVE_LIBIEEE1284)
+
+ DBG (4, "pa4s2_init: initializing libieee1284\n");
+ result = ieee1284_find_ports (&pplist, 0);
+
+ if (result)
+ {
+ DBG (1, "pa4s2_init: initializing IEEE 1284 failed (%s)\n",
+ pa4s2_libieee1284_errorstr (result));
+ first_time = SANE_TRUE;
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ DBG (3, "pa4s2_init: %d ports reported by IEEE 1284 library\n", pplist.portc);
+
+ for (n=0; n<pplist.portc; n++)
+ DBG (6, "pa4s2_init: port %d is `%s`\n", n, pplist.portv[n]->name);
+
+
+ DBG (6, "pa4s2_init: allocating port list\n");
+ if ((port = calloc(pplist.portc, sizeof(PortRec))) == NULL)
+ {
+ DBG (1, "pa4s2_init: not enough free memory\n");
+ ieee1284_free_ports(&pplist);
+ first_time = SANE_TRUE;
+ *status = SANE_STATUS_NO_MEM;
+ return -1;
+ }
+
+#else
+
+ DBG (4, "pa4s2_init: trying to setuid root\n");
+
+ if (0 > setuid (0))
+ {
+
+ DBG (1, "pa4s2_init: setuid failed: errno = %d\n", errno);
+ DBG (5, "pa4s2_init: returning SANE_STATUS_INVAL\n");
+
+ *status = SANE_STATUS_INVAL;
+ first_time = SANE_TRUE;
+ return -1;
+
+ }
+
+ DBG (3, "pa4s2_init: the application is now root\n");
+ DBG (3, "pa4s2_init: this is a high security risk...\n");
+
+ DBG (6, "pa4s2_init: ... you'd better start praying\n");
+
+ /* PS: no, i don't trust myself either */
+
+ /* PPS: i'd try rsbac or similar if i were you */
+
+#endif
+
+ DBG (5, "pa4s2_init: initialized successfully\n");
+ *status = SANE_STATUS_GOOD;
+ return 0;
+}
+
+static int
+pa4s2_open (const char *dev, SANE_Status * status)
+{
+
+ int n, result;
+#if !defined (HAVE_LIBIEEE1284)
+ u_long base;
+#endif
+
+ DBG (4, "pa4s2_open: trying to attach dev `%s`\n", dev);
+
+ if ((result = pa4s2_init(status)) != 0)
+ {
+
+ DBG (1, "pa4s2_open: failed to initialize\n");
+ return result;
+ }
+
+#if !defined (HAVE_LIBIEEE1284)
+
+ {
+ char *end;
+
+ DBG (5, "pa4s2_open: reading port number\n");
+
+ base = strtol (dev, &end, 0);
+
+ if ((end == dev) || (*end != '\0'))
+ {
+
+ DBG (1, "pa4s2_open: `%s` is not a valid port number\n", dev);
+ DBG (6, "pa4s2_open: the part I did not understand was ...`%s`\n", end);
+ DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");
+
+ *status = SANE_STATUS_INVAL;
+
+ return -1;
+
+ }
+
+ }
+
+ DBG (6, "pa4s2_open: read port number 0x%03lx\n", base);
+
+ if (base == 0)
+ {
+
+ DBG (1, "pa4s2_open: 0x%03lx is not a valid base address\n", base);
+ DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");
+
+ *status = SANE_STATUS_INVAL;
+ return -1;
+
+ }
+#endif
+
+ DBG (5, "pa4s2_open: looking up port in list\n");
+
+#if defined (HAVE_LIBIEEE1284)
+
+ for (n = 0; n < pplist.portc; n++)
+ if (!strcmp(pplist.portv[n]->name, dev))
+ break;
+
+ if (pplist.portc <= n)
+ {
+ DBG (1, "pa4s2_open: `%s` is not a valid device name\n", dev);
+ DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");
+
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+#else
+
+ for (n = 0; n < NELEMS (port); n++)
+ if (port[n].base == base)
+ break;
+
+ if (NELEMS (port) <= n)
+ {
+
+ DBG (1, "pa4s2_open: 0x%03lx is not a valid base address\n",
+ base);
+ DBG (5, "pa4s2_open: returning SANE_STATUS_INVAL\n");
+
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+#endif
+
+ DBG (6, "pa4s2_open: port is in list at port[%d]\n", n);
+
+ if (port[n].in_use == SANE_TRUE)
+ {
+
+#if defined (HAVE_LIBIEEE1284)
+ DBG (1, "pa4s2_open: device `%s` is already in use\n", dev);
+#else
+ DBG (1, "pa4s2_open: port 0x%03lx is already in use\n", base);
+#endif
+ DBG (5, "pa4s2_open: returning SANE_STATUS_DEVICE_BUSY\n");
+
+ *status = SANE_STATUS_DEVICE_BUSY;
+ return -1;
+
+ }
+
+ DBG (5, "pa4s2_open: setting up port data\n");
+
+#if defined (HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_open: name=%s in_use=SANE_TRUE\n", dev);
+#else
+ DBG (6, "pa4s2_open: base=0x%03lx in_use=SANE_TRUE\n", base);
+#endif
+ DBG (6, "pa4s2_open: enabled=SANE_FALSE mode=PA4S2_MODE_NIB\n");
+ port[n].in_use = SANE_TRUE;
+ port[n].enabled = SANE_FALSE;
+ port[n].mode = PA4S2_MODE_NIB;
+
+
+#if defined (HAVE_LIBIEEE1284)
+
+ DBG (5, "pa4s2_open: opening device\n");
+ result = ieee1284_open (pplist.portv[n], 0, &port[n].caps);
+
+ if (result)
+ {
+ DBG (1, "pa4s2_open: could not open device `%s` (%s)\n",
+ dev, pa4s2_libieee1284_errorstr (result));
+ port[n].in_use = SANE_FALSE;
+ DBG (6, "pa4s2_open: marking port %d as unused\n", n);
+ *status = SANE_STATUS_ACCESS_DENIED;
+ return -1;
+ }
+
+#else
+
+ DBG (5, "pa4s2_open: getting io permissions\n");
+
+ /* TODO: insert FreeBSD compatible code here */
+
+ if (ioperm (port[n].base, 5, 1))
+ {
+
+ DBG (1, "pa4s2_open: cannot get io privilege for port 0x%03lx\n",
+ port[n].base);
+
+
+ DBG (5, "pa4s2_open: marking port[%d] as unused\n", n);
+ port[n].in_use = SANE_FALSE;
+
+ DBG (5, "pa4s2_open: returning SANE_STATUS_IO_ERROR\n");
+ *status = SANE_STATUS_IO_ERROR;
+ return -1;
+
+ }
+#endif
+
+ DBG (3, "pa4s2_open: device `%s` opened...\n", dev);
+
+ DBG (5, "pa4s2_open: returning SANE_STATUS_GOOD\n");
+ *status = SANE_STATUS_GOOD;
+
+ DBG (4, "pa4s2_open: open dev `%s` as fd %u\n", dev, n);
+
+ return n;
+
+}
+
+#if defined(HAVE_LIBIEEE1284)
+
+
+#define inbyte0(fd) ieee1284_read_data(pplist.portv[fd]);
+#define inbyte1(fd) (ieee1284_read_status(pplist.portv[fd]) ^ S1284_INVERTED)
+#define inbyte2(fd) (ieee1284_read_control(pplist.portv[fd]) ^ C1284_INVERTED)
+static u_char inbyte4(int fd)
+{
+ char val;
+ ieee1284_epp_read_data(pplist.portv[fd], 0, &val, 1);
+ return (u_char)val;
+}
+
+#define outbyte0(fd,val) ieee1284_write_data(pplist.portv[fd], val)
+#define outbyte1(fd,val) /* ieee1284_write_status(pplist.portv[fd], (val) ^ S1284_INVERTED) */
+#define outbyte2(fd,val) ieee1284_write_control(pplist.portv[fd], (val) ^ C1284_INVERTED)
+
+static void outbyte3(int fd, u_char val)
+{
+ ieee1284_epp_write_addr (pplist.portv[fd], 0, (char *)&val, 1);
+}
+
+#else
+
+#define inbyte0(fd) inb(port[fd].base)
+#define inbyte1(fd) inb(port[fd].base + 1)
+#define inbyte2(fd) inb(port[fd].base + 2)
+#define inbyte4(fd) inb(port[fd].base + 4)
+
+#define outbyte0(fd,val) outb(val, port[fd].base)
+#define outbyte1(fd,val) outb(val, port[fd].base + 1)
+#define outbyte2(fd,val) outb(val, port[fd].base + 2)
+#define outbyte3(fd,val) outb(val, port[fd].base + 3)
+
+#endif
+
+
+static void
+pa4s2_readbegin_epp (int fd, u_char reg)
+{
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_readbegin_epp: selecting register %u at '%s'\n",
+ (int) reg, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_readbegin_epp: selecting register %u at 0x%03lx\n",
+ (int) reg, port[fd].base);
+#endif
+
+ outbyte0 (fd, 0x20);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x04);
+ outbyte3 (fd, reg + 0x18);
+
+}
+
+static u_char
+pa4s2_readbyte_epp (int fd)
+{
+
+ u_char val = inbyte4 (fd);
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_readbyte_epp: reading value 0x%02x from '%s'\n",
+ (int) val, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_readbyte_epp: reading value 0x%02x at 0x%03lx\n",
+ (int) val, port[fd].base);
+#endif
+
+ return val;
+
+}
+
+static void
+pa4s2_readend_epp (int fd)
+{
+
+ DBG (6, "pa4s2_readend_epp: end of reading sequence\n");
+
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x00);
+ outbyte2 (fd, 0x04);
+
+}
+
+static void
+pa4s2_readbegin_uni (int fd, u_char reg)
+{
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_readbegin_uni: selecting register %u for '%s'\n",
+ (int) reg, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_readbegin_uni: selecting register %u at 0x%03lx\n",
+ (int) reg, port[fd].base);
+#endif
+
+ outbyte0 (fd, reg | 0x58);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+
+}
+
+static u_char
+pa4s2_readbyte_uni (int fd)
+{
+ u_char val;
+
+ outbyte2 (fd, 0x05);
+ val = inbyte2(fd);
+ val <<= 4;
+ val &= 0xE0;
+ val |= (inbyte1(fd) >> 3);
+ outbyte2 (fd, 0x04);
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_readbyte_uni: reading value 0x%02x from '%s'\n",
+ (int) val, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_readbyte_uni: reading value 0x%02x at 0x%03lx\n",
+ (int) val, port[fd].base);
+#endif
+
+ return val;
+}
+
+static void
+pa4s2_readend_uni (int fd)
+{
+
+ DBG (6, "pa4s2_readend_uni: end of reading sequence for fd %d\n", fd);
+
+}
+
+static void
+pa4s2_readbegin_nib (int fd, u_char reg)
+{
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_readbegin_nib: selecting register %u at '%s'\n",
+ (int) reg, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_readbegin_nib: selecting register %u at 0x%03lx\n",
+ (int) reg, port[fd].base);
+#endif
+
+
+ outbyte0 (fd, reg | 0x18);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+
+}
+
+static u_char
+pa4s2_readbyte_nib (int fd)
+{
+
+ u_char val;
+
+ outbyte2 (fd, 0x05);
+ val = inbyte1(fd);
+ val >>= 4;
+ outbyte0 (fd, 0x58);
+ val |= inbyte1(fd) & 0xF0;
+ val ^= 0x88;
+ outbyte0 (fd, 0x00);
+ outbyte2 (fd, 0x04);
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_readbyte_nib: reading value 0x%02x from '%s'\n",
+ (int) val, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_readbyte_nib: reading value 0x%02x at 0x%03lx\n",
+ (int) val, port[fd].base);
+#endif
+
+ return val;
+
+}
+
+static void
+pa4s2_readend_nib (int fd)
+{
+ DBG (6, "pa4s2_readend_nib: end of reading sequence for fd %d\n", fd);
+}
+
+static void
+pa4s2_writebyte_any (int fd, u_char reg, u_char val)
+{
+
+ /* somebody from Mustek asked me once, why I was writing the same
+ value repeatedly to a port. Well, actually I don't know, it just
+ works. Maybe the repeated writes could be replaced by appropriate
+ delays or even left out completly.
+ */
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_writebyte_any: writing value 0x%02x"
+ " in reg %u to '%s'\n", (int) val, (int) reg, pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_writebyte_any: writing value 0x%02x"
+ " in reg %u at 0x%03lx\n", (int) val, (int) reg, port[fd].base);
+#endif
+
+ outbyte0 (fd, reg | 0x10);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+ outbyte0 (fd, val);
+ outbyte2 (fd, 0x05);
+ outbyte2 (fd, 0x05);
+ outbyte2 (fd, 0x05);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+}
+
+static int
+pa4s2_enable (int fd, u_char * prelock)
+{
+#if defined (HAVE_LIBIEEE1284)
+ int result;
+ result = ieee1284_claim (pplist.portv[fd]);
+
+ if (result)
+ {
+ DBG (1, "pa4s2_enable: failed to claim the port (%s)\n",
+ pa4s2_libieee1284_errorstr(result));
+ return -1;
+ }
+#endif
+
+ prelock[0] = inbyte0 (fd);
+ prelock[1] = inbyte1 (fd);
+ prelock[2] = inbyte2 (fd);
+ outbyte2 (fd, (prelock[2] & 0x0F) | 0x04);
+
+ DBG (6, "pa4s2_enable: prelock[] = {0x%02x, 0x%02x, 0x%02x}\n",
+ (int) prelock[0], (int) prelock[1], (int) prelock[2]);
+
+ outbyte0 (fd, 0x15);
+ outbyte0 (fd, 0x95);
+ outbyte0 (fd, 0x35);
+ outbyte0 (fd, 0xB5);
+ outbyte0 (fd, 0x55);
+ outbyte0 (fd, 0xD5);
+ outbyte0 (fd, 0x75);
+ outbyte0 (fd, 0xF5);
+ outbyte0 (fd, 0x01);
+ outbyte0 (fd, 0x81);
+
+ return 0;
+}
+
+static int
+pa4s2_disable (int fd, u_char * prelock)
+{
+
+ if ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_ALT_LOCK) != 0)
+ {
+
+ DBG (6, "pa4s2_disable: using alternative command set\n");
+
+ outbyte0 (fd, 0x00);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x04);
+
+ }
+
+ outbyte2 (fd, prelock[2] & 0x0F);
+
+ outbyte0 (fd, 0x15);
+ outbyte0 (fd, 0x95);
+ outbyte0 (fd, 0x35);
+ outbyte0 (fd, 0xB5);
+ outbyte0 (fd, 0x55);
+ outbyte0 (fd, 0xD5);
+ outbyte0 (fd, 0x75);
+ outbyte0 (fd, 0xF5);
+ outbyte0 (fd, 0x00);
+ outbyte0 (fd, 0x80);
+
+ outbyte0 (fd, prelock[0]);
+ outbyte1 (fd, prelock[1]);
+ outbyte2 (fd, prelock[2]);
+
+#if defined(HAVE_LIBIEEE1284)
+ ieee1284_release (pplist.portv[fd]);
+#endif
+
+ DBG (6, "pa4s2_disable: state restored\n");
+
+ return 0;
+
+}
+
+static int
+pa4s2_close (int fd, SANE_Status * status)
+{
+#if defined(HAVE_LIBIEEE1284)
+ int result;
+#endif
+ DBG (4, "pa4s2_close: fd=%d\n", fd);
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "pa4s2_close: this is port '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "pa4s2_close: this is port 0x%03lx\n", port[fd].base);
+#endif
+
+ DBG (5, "pa4s2_close: checking whether port is enabled\n");
+
+ if (port[fd].enabled == SANE_TRUE)
+ {
+
+ DBG (6, "pa4s2_close: disabling port\n");
+ pa4s2_disable (fd, port[fd].prelock);
+
+ }
+
+ DBG (5, "pa4s2_close: trying to free io port\n");
+#if defined(HAVE_LIBIEEE1284)
+ if ((result = ieee1284_close(pplist.portv[fd])) < 0)
+#else
+ if (ioperm (port[fd].base, 5, 0))
+#endif
+ {
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (1, "pa4s2_close: can't free port '%s' (%s)\n",
+ pplist.portv[fd]->name, pa4s2_libieee1284_errorstr(result));
+#else
+ DBG (1, "pa4s2_close: can't free port 0x%03lx\n", port[fd].base);
+#endif
+
+ DBG (5, "pa4s2_close: returning SANE_STATUS_IO_ERROR\n");
+ *status = SANE_STATUS_IO_ERROR;
+ return -1;
+
+ }
+
+ DBG (5, "pa4s2_close: marking port as unused\n");
+
+ port[fd].in_use = SANE_FALSE;
+
+ DBG (5, "pa4s2_close: returning SANE_STATUS_GOOD\n");
+
+ *status = SANE_STATUS_GOOD;
+
+ return 0;
+
+}
+
+const char **
+sanei_pa4s2_devices()
+{
+
+ SANE_Status status;
+ int n;
+ const char **devices;
+
+ TEST_DBG_INIT();
+
+ DBG (4, "sanei_pa4s2_devices: invoked\n");
+
+ if ((n = pa4s2_init(&status)) != 0)
+ {
+
+ DBG (1, "sanei_pa4s2_devices: failed to initialize (%s)\n",
+ sane_strstatus(status));
+ return calloc(1, sizeof(char *));
+ }
+
+#if defined(HAVE_LIBIEEE1284)
+
+ if ((devices = calloc((pplist.portc + 1), sizeof(char *))) == NULL)
+ {
+ DBG (2, "sanei_pa4s2_devices: not enough free memory\n");
+ return calloc(1, sizeof(char *));
+ }
+
+ for (n=0; n<pplist.portc; n++)
+ devices[n] = pplist.portv[n]->name;
+
+#else
+
+ if ((devices = calloc((NELEMS (port) + 1), sizeof(char *))) == NULL)
+ {
+ DBG (2, "sanei_pa4s2_devices: not enough free memory\n");
+ return calloc(1, sizeof(char *));
+ }
+
+ for (n=0 ; n<NELEMS (port) ; n++)
+ devices[n] = (char *)port[n].name;
+#endif
+
+ return devices;
+}
+
+/*
+ * Needed for SCSI-over-parallel scanners (Paragon 600 II EP)
+ */
+SANE_Status
+sanei_pa4s2_scsi_pp_get_status(int fd, u_char *status)
+{
+ u_char stat;
+
+ TEST_DBG_INIT ();
+
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: called for fd %d\n",
+ fd);
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_scsi_pp_get_status: invalid fd %d\n", fd);
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_scsi_pp_get_status: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
+ pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
+ port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].enabled == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_scsi_pp_get_status: port is not enabled\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
+ pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
+ port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ outbyte2 (fd, 0x4);
+ stat = inbyte1 (fd)^0x80;
+ *status = (stat&0x2f)|((stat&0x10)<<2)|((stat&0x40)<<1)|((stat&0x80)>>3);
+ DBG (5, "sanei_pa4s2_scsi_pp_get_status: status=0x%02X\n", *status);
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * SCSI-over-parallel scanners need this done when a register is
+ * selected
+ */
+SANE_Status
+sanei_pa4s2_scsi_pp_reg_select (int fd, int reg)
+{
+ TEST_DBG_INIT ();
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_scsi_pp_reg_select: invalid fd %d\n", fd);
+ DBG (6, "sanei_pa4s2_scsi_pp_reg_select: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_scsi_pp_reg_select: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
+ pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
+ port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_scsi_pp_reg_select: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].enabled == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_scsi_pp_reg_select: port is not enabled\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (4, "sanei_pa4s2_scsi_pp_get_status: port is '%s'\n",
+ pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_scsi_pp_get_status: port is 0x%03lx\n",
+ port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_scsi_pp_reg_select: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_scsi_pp_reg_select: selecting register %u at port '%s'\n",
+ (int) reg, pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_scsi_pp_reg_select: selecting register %u at 0x%03lx\n",
+ (int) reg, (u_long)port[fd].base);
+#endif
+
+ outbyte0 (fd, reg | 0x58);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x06);
+ outbyte2 (fd, 0x04);
+ outbyte2 (fd, 0x04);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * The SCSI-over-parallel scanners need to be handled a bit differently
+ * when opened, as they don't return a valid ASIC ID, so this can't be
+ * used for detecting valid read modes
+ */
+SANE_Status
+sanei_pa4s2_scsi_pp_open (const char *dev, int *fd)
+{
+
+ u_char val;
+ SANE_Status status;
+
+ TEST_DBG_INIT ();
+
+ DBG(4, "sanei_pa4s2_scsi_pp_open: called for device '%s'\n", dev);
+ DBG(5, "sanei_pa4s2_scsi_pp_open: trying to connect to port\n");
+
+ if ((*fd = pa4s2_open (dev, &status)) == -1)
+ {
+
+ DBG (5, "sanei_pa4s2_scsi_pp_open: connection failed\n");
+
+ return status;
+
+ }
+
+ DBG (6, "sanei_pa4s2_scsi_pp_open: connected to device using fd %u\n", *fd);
+
+ DBG (5, "sanei_pa4s2_scsi_pp_open: checking for scanner\n");
+
+ if (sanei_pa4s2_enable (*fd, SANE_TRUE)!=SANE_STATUS_GOOD)
+ {
+ DBG (3, "sanei_pa4s2_scsi_pp_open: error enabling device\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /*
+ * Instead of checking ASIC ID, check device status
+ */
+ if (sanei_pa4s2_scsi_pp_get_status(*fd, &val)!=SANE_STATUS_GOOD)
+ {
+ DBG (3, "sanei_pa4s2_scsi_pp_open: error getting device status\n");
+ sanei_pa4s2_enable (*fd, SANE_FALSE);
+ return SANE_STATUS_IO_ERROR;
+ }
+ val&=0xf0;
+
+ if ((val==0xf0)||(val&0x40)||(!(val&0x20)))
+ {
+ DBG (3, "sanei_pa4s2_scsi_pp_open: device returned status 0x%02X\n", val);
+ sanei_pa4s2_enable (*fd, SANE_FALSE);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (sanei_pa4s2_enable (*fd, SANE_FALSE)!=SANE_STATUS_GOOD)
+ {
+ DBG (3, "sanei_pa4s2_scsi_pp_open: error disabling device\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* FIXME: it would be nice to try to use a better mode here, but how to
+ * know if it's going to work? */
+
+ DBG (4, "sanei_pa4s2_scsi_pp_open: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pa4s2_open (const char *dev, int *fd)
+{
+
+ u_char asic, val;
+ SANE_Status status;
+
+ TEST_DBG_INIT ();
+
+ DBG(4, "sanei_pa4s2_open: called for device '%s'\n", dev);
+ DBG(5, "sanei_pa4s2_open: trying to connect to port\n");
+
+ if ((*fd = pa4s2_open (dev, &status)) == -1)
+ {
+
+ DBG (5, "sanei_pa4s2_open: connection failed\n");
+
+ return status;
+
+ }
+
+ DBG (6, "sanei_pa4s2_open: connected to device using fd %u\n", *fd);
+
+ DBG (5, "sanei_pa4s2_open: checking for scanner\n");
+
+ sanei_pa4s2_enable (*fd, SANE_TRUE);
+
+ DBG (6, "sanei_pa4s2_open: reading ASIC id\n");
+
+ sanei_pa4s2_readbegin (*fd, 0);
+
+ sanei_pa4s2_readbyte (*fd, &asic);
+
+ sanei_pa4s2_readend (*fd);
+
+ switch (asic)
+ {
+
+ case PA4S2_ASIC_ID_1013:
+ DBG (3, "sanei_pa4s2_open: detected ASIC id 1013\n");
+ break;
+
+ case PA4S2_ASIC_ID_1015:
+ DBG (3, "sanei_pa4s2_open: detected ASIC id 1015\n");
+ break;
+
+ case PA4S2_ASIC_ID_1505:
+ DBG (3, "sanei_pa4s2_open: detected ASIC id 1505\n");
+ break;
+
+ default:
+ DBG (1, "sanei_pa4s2_open: could not find scanner\n");
+ DBG (3, "sanei_pa4s2_open: reported ASIC id 0x%02x\n",
+ asic);
+
+ sanei_pa4s2_enable (*fd, SANE_FALSE);
+ DBG (5, "sanei_pa4s2_open: closing port\n");
+
+ sanei_pa4s2_close (*fd);
+
+ DBG (5, "sanei_pa4s2_open: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ sanei_pa4s2_enable (*fd, SANE_FALSE);
+
+ DBG (4, "sanei_pa4s2_open: trying better modes\n");
+
+ while (port[*fd].mode <= PA4S2_MODE_EPP)
+ {
+
+ if ((port[*fd].mode == PA4S2_MODE_UNI) &&
+ ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_TRY_MODE_UNI) == 0))
+ {
+
+ DBG (3, "sanei_pa4s2_open: skipping mode UNI\n");
+ port[*fd].mode++;
+ continue;
+
+ }
+
+ if ((port[*fd].mode == PA4S2_MODE_EPP) &&
+ ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_NO_EPP) != 0))
+ {
+ DBG (3, "sanei_pa4s2_open: skipping mode EPP\n");
+ break;
+ }
+
+
+ DBG (5, "sanei_pa4s2_open: trying mode %u\n", port[*fd].mode);
+
+ sanei_pa4s2_enable (*fd, SANE_TRUE);
+
+ sanei_pa4s2_readbegin (*fd, 0);
+
+ sanei_pa4s2_readbyte (*fd, &val);
+
+ if (val != asic)
+ {
+
+ sanei_pa4s2_readend (*fd);
+ sanei_pa4s2_enable (*fd, SANE_FALSE);
+ DBG (5, "sanei_pa4s2_open: mode failed\n");
+ DBG (6, "sanei_pa4s2_open: returned ASIC-ID 0x%02x\n",
+ (int) val);
+ break;
+
+ }
+
+ sanei_pa4s2_readend (*fd);
+ sanei_pa4s2_enable (*fd, SANE_FALSE);
+
+ DBG (5, "sanei_pa4s2_open: mode works\n");
+
+ port[*fd].mode++;
+
+ }
+
+ port[*fd].mode--;
+
+ if ((port[*fd].mode == PA4S2_MODE_UNI) &&
+ ((sanei_pa4s2_interface_options & SANEI_PA4S2_OPT_TRY_MODE_UNI) == 0))
+ {
+ port[*fd].mode--;
+ }
+
+ DBG (5, "sanei_pa4s2_open: using mode %u\n", port[*fd].mode);
+
+ DBG (4, "sanei_pa4s2_open: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+
+}
+
+void
+sanei_pa4s2_close (int fd)
+{
+
+ SANE_Status status;
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_close: fd = %d\n", fd);
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_close: fd %d is invalid\n", fd);
+ DBG (5, "sanei_pa4s2_close: failed\n");
+ return;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_close: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_close: failed\n");
+ return;
+
+ }
+
+ DBG (5, "sanei_pa4s2_close: freeing resources\n");
+
+ if (pa4s2_close (fd, &status) == -1)
+ {
+
+ DBG (2, "sanei_pa4s2_close: could not close scanner\n");
+ DBG (5, "sanei_pa4s2_close: failed\n");
+ return;
+ }
+
+ DBG (5, "sanei_pa4s2_close: finished\n");
+
+}
+
+SANE_Status
+sanei_pa4s2_enable (int fd, int enable)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_enable: called for fd %d with value %d\n",
+ fd, enable);
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_enable: fd %d is invalid\n", fd);
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_enable: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if ((enable != SANE_TRUE) && (enable != SANE_FALSE))
+ {
+
+ DBG (2, "sanei_pa4s2_enable: invalid value %d\n", enable);
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if ((unsigned int) enable == port[fd].enabled)
+ {
+
+ DBG (3, "sanei_pa4s2_enable: senseless call...\n");
+ DBG (4, "sanei_pa4s2_enable: aborting\n");
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+
+ }
+
+ if (enable == SANE_TRUE)
+ {
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (4, "sanei_pa4s2_enable: enable port '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (4, "sanei_pa4s2_enable: enable port 0x%03lx\n", port[fd].base);
+
+ /* io-permissions are not inherited after fork (at least not on
+ linux 2.2, although they seem to be inherited on linux 2.4),
+ so we should make sure we get the permission */
+
+ if (ioperm (port[fd].base, 5, 1))
+ {
+ DBG (1, "sanei_pa4s2_enable: cannot get io privilege for port"
+ " 0x%03lx\n", port[fd].base);
+
+ DBG (5, "sanei_pa4s2_enable:: marking port[%d] as unused\n", fd);
+ port[fd].in_use = SANE_FALSE;
+
+ DBG (5, "sanei_pa4s2_enable:: returning SANE_STATUS_IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+#endif
+
+ if (pa4s2_enable (fd, port[fd].prelock) != 0)
+ {
+ DBG (1, "sanei_pa4s2_enable: failed to enable port\n");
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_IO_ERROR\n");
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ }
+ else
+ {
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG (4, "sanei_pa4s2_enable: disable port '%s'\n",
+ pplist.portv[fd]->name);
+#else
+ DBG (4, "sanei_pa4s2_enable: disable port 0x%03lx\n", port[fd].base);
+#endif
+
+ pa4s2_disable (fd, port[fd].prelock);
+
+ }
+
+ port[fd].enabled = enable;
+
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pa4s2_readbegin (int fd, u_char reg)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_readbegin: called for fd %d and register %u\n",
+ fd, (int) reg);
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_readbegin: invalid fd %d\n", fd);
+ DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_readbegin: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].enabled == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_readbegin: port is not enabled\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ switch (port[fd].mode)
+ {
+
+ case PA4S2_MODE_EPP:
+
+ DBG (5, "sanei_pa4s2_readbegin: EPP readbegin\n");
+ pa4s2_readbegin_epp (fd, reg);
+ break;
+
+ case PA4S2_MODE_UNI:
+
+ DBG (5, "sanei_pa4s2_readbegin: UNI readbegin\n");
+ pa4s2_readbegin_uni (fd, reg);
+ break;
+
+ case PA4S2_MODE_NIB:
+
+ DBG (5, "sanei_pa4s2_readbegin: NIB readbegin\n");
+ pa4s2_readbegin_nib (fd, reg);
+ break;
+
+ default:
+
+ DBG (1, "sanei_pa4s2_readbegin: port info broken\n");
+ DBG (3, "sanei_pa4s2_readbegin: invalid port mode\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readbegin: return SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pa4s2_readbyte (int fd, u_char * val)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_readbyte: called with fd %d\n", fd);
+
+ if (val == NULL)
+ {
+
+ DBG (1, "sanei_pa4s2_readbyte: got NULL pointer as result buffer\n");
+ return SANE_STATUS_INVAL;
+
+ }
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_readbyte: invalid fd %d\n", fd);
+ DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_readbyte: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].enabled == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_readbyte: port is not enabled\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ DBG (4, "sanei_pa4s2_readbyte: we hope, the backend called\n");
+ DBG (4, "sanei_pa4s2_readbyte: readbegin, so the port is ok...\n");
+
+ DBG (6, "sanei_pa4s2_readbyte: this means, I did not check it - it's\n");
+ DBG (6, "sanei_pa4s2_readbyte: not my fault, if your PC burns down.\n");
+
+ switch (port[fd].mode)
+ {
+
+ case PA4S2_MODE_EPP:
+
+ DBG (5, "sanei_pa4s2_readbyte: read in EPP mode\n");
+ *val = pa4s2_readbyte_epp (fd);
+ break;
+
+
+ case PA4S2_MODE_UNI:
+
+ DBG (5, "sanei_pa4s2_readbyte: read in UNI mode\n");
+ *val = pa4s2_readbyte_uni (fd);
+ break;
+
+
+ case PA4S2_MODE_NIB:
+
+ DBG (5, "sanei_pa4s2_readbyte: read in NIB mode\n");
+ *val = pa4s2_readbyte_nib (fd);
+ break;
+
+ default:
+
+ DBG (1, "sanei_pa4s2_readbyte: port info broken\n");
+ DBG (2, "sanei_pa4s2_readbyte: probably the port wasn't"
+ " correct configured...\n");
+ DBG (3, "sanei_pa4s2_readbyte: invalid port mode\n");
+ DBG (6, "sanei_pa4s2_readbyte: port mode %u\n",
+ port[fd].mode);
+ DBG (6, "sanei_pa4s2_readbyte: I told you!!!\n");
+ DBG (5, "sanei_pa4s2_readbyte: return"
+ " SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_pa4s2_readbyte: read finished\n");
+
+ DBG (6, "sanei_pa4s2_readbyte: got value 0x%02x\n", (int) *val);
+
+ DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sanei_pa4s2_readend (int fd)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_readend: called for fd %d\n", fd);
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_readend: invalid fd %d\n", fd);
+ DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_readend: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].enabled == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_readend: port is not enabled\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ DBG (4, "sanei_pa4s2_readend: we hope, the backend called\n");
+ DBG (4, "sanei_pa4s2_readend: readbegin, so the port is ok...\n");
+
+ DBG (6, "sanei_pa4s2_readend: this means, I did not check it - it's\n");
+ DBG (6, "sanei_pa4s2_readend: not my fault, if your PC burns down.\n");
+
+ switch (port[fd].mode)
+ {
+
+ case PA4S2_MODE_EPP:
+
+ DBG (5, "sanei_pa4s2_readend: EPP mode readend\n");
+ pa4s2_readend_epp (fd);
+ break;
+
+
+ case PA4S2_MODE_UNI:
+
+ DBG (5, "sanei_pa4s2_readend: UNI mode readend\n");
+ pa4s2_readend_uni (fd);
+ break;
+
+
+ case PA4S2_MODE_NIB:
+
+ DBG (5, "sanei_pa4s2_readend: NIB mode readend\n");
+ pa4s2_readend_nib (fd);
+ break;
+
+ default:
+
+ DBG (1, "sanei_pa4s2_readend: port info broken\n");
+ DBG (2, "sanei_pa4s2_readend: probably the port wasn't"
+ " correct configured...\n");
+ DBG (3, "sanei_pa4s2_readend: invalid port mode\n");
+ DBG (6, "sanei_pa4s2_readend: port mode %u\n",
+ port[fd].mode);
+ DBG (6, "sanei_pa4s2_readend: I told you!!!\n");
+ DBG (5, "sanei_pa4s2_readend: return"
+ " SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+
+ DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sanei_pa4s2_writebyte (int fd, u_char reg, u_char val)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_writebyte: called for fd %d, reg %u and val %u\n",
+ fd, (int) reg, (int) val);
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc))
+#else
+ if ((fd < 0) || (fd >= NELEMS (port)))
+#endif
+ {
+
+ DBG (2, "sanei_pa4s2_writebyte: invalid fd %d\n", fd);
+ DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].in_use == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_writebyte: port is not in use\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ if (port[fd].enabled == SANE_FALSE)
+ {
+
+ DBG (2, "sanei_pa4s2_writebyte: port is not enabled\n");
+#if defined(HAVE_LIBIEEE1284)
+ DBG (6, "sanei_pa4s2_close: port is '%s'\n", pplist.portv[fd]->name);
+#else
+ DBG (6, "sanei_pa4s2_close: port is 0x%03lx\n", port[fd].base);
+#endif
+ DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ switch (port[fd].mode)
+ {
+
+ case PA4S2_MODE_EPP:
+ case PA4S2_MODE_UNI:
+ case PA4S2_MODE_NIB:
+
+ DBG (5, "sanei_pa4s2_writebyte: NIB/UNI/EPP write\n");
+ pa4s2_writebyte_any (fd, reg, val);
+ break;
+
+ default:
+
+ DBG (1, "sanei_pa4s2_writebyte: port info broken\n");
+ DBG (3, "sanei_pa4s2_writebyte: invalid port mode\n");
+ DBG (6, "sanei_pa4s2_writebyte: port mode %u\n",
+ port[fd].mode);
+ DBG (5, "sanei_pa4s2_writebyte: return"
+ " SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+ }
+
+ DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pa4s2_options (u_int * options, int set)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_options: called with options %u and set = %d\n",
+ *options, set);
+
+ if ((set != SANE_TRUE) && (set != SANE_FALSE))
+ DBG (2, "sanei_pa4s2_options: value of set is invalid\n");
+
+ if ((set == SANE_TRUE) && (*options > 7))
+ DBG (2, "sanei_pa4s2_options: value of *options is invalid\n");
+
+ if (set == SANE_TRUE)
+ {
+
+ DBG (5, "sanei_pa4s2_options: setting options to %u\n", *options);
+
+ sanei_pa4s2_interface_options = *options;
+
+ }
+ else
+ {
+
+ DBG (5, "sanei_pa4s2_options: options are set to %u\n",
+ sanei_pa4s2_interface_options);
+
+ *options = sanei_pa4s2_interface_options;
+
+ }
+
+ DBG (5, "sanei_pa4s2_options: returning SANE_STATUS_GOOD\n");
+
+ return SANE_STATUS_GOOD;
+
+}
+
+#else /* !HAVE_IOPERM */
+
+
+SANE_Status
+sanei_pa4s2_open (const char *dev, int *fd)
+{
+
+ TEST_DBG_INIT ();
+
+ if (fd)
+ *fd = -1;
+
+ DBG (4, "sanei_pa4s2_open: called for device `%s`\n", dev);
+ DBG (3, "sanei_pa4s2_open: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_open: basically, this backend does only compile\n");
+ DBG (6, "sanei_pa4s2_open: on x86 architectures. Furthermore it\n");
+ DBG (6, "sanei_pa4s2_open: needs ioperm() and inb()/outb() calls.\n");
+ DBG (6, "sanei_pa4s2_open: alternativly it makes use of libieee1284\n");
+ DBG (6, "sanei_pa4s2_open: (which isn't present either)\n");
+ DBG (5, "sanei_pa4s2_open: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+}
+
+void
+sanei_pa4s2_close (int fd)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_close: called for fd %d\n", fd);
+ DBG (2, "sanei_pa4s2_close: fd %d is invalid\n", fd);
+ DBG (3, "sanei_pa4s2_close: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_close: so I wonder, why this function is called"
+ " anyway.\n");
+ DBG (6, "sanei_pa4s2_close: maybe this is a bug in the backend.\n");
+ DBG (5, "sanei_pa4s2_close: returning\n");
+
+ return;
+}
+
+SANE_Status
+sanei_pa4s2_enable (int fd, int enable)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_enable: called for fd %d with value=%d\n",
+ fd, enable);
+ DBG (2, "sanei_pa4s2_enable: fd %d is invalid\n", fd);
+
+ if ((enable != SANE_TRUE) && (enable != SANE_FALSE))
+ DBG (2, "sanei_pa4s2_enable: value %d is invalid\n", enable);
+
+ DBG (3, "sanei_pa4s2_enable: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_enable: oops, I think there's someone going to\n");
+ DBG (6, "sanei_pa4s2_enable: produce a lot of garbage...\n");
+ DBG (5, "sanei_pa4s2_enable: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pa4s2_readbegin (int fd, u_char reg)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_readbegin: called for fd %d and register %d\n",
+ fd, (int) reg);
+ DBG (2, "sanei_pa4s2_readbegin: fd %d is invalid\n", fd);
+
+ DBG (3, "sanei_pa4s2_readbegin: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_readbegin: don't look - this is going to be\n");
+ DBG (6, "sanei_pa4s2_readbegin: worse then you'd expect...\n");
+ DBG (5, "sanei_pa4s2_readbegin: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+}
+
+SANE_Status
+sanei_pa4s2_readbyte (int fd, u_char * val)
+{
+
+ TEST_DBG_INIT ();
+
+ if (val)
+ *val = 0;
+
+ DBG (4, "sanei_pa4s2_readbyte: called for fd %d\n", fd);
+ DBG (2, "sanei_pa4s2_readbyte: fd %d is invalid\n", fd);
+ DBG (3, "sanei_pa4s2_readbyte: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_readbyte: shit happens\n");
+ DBG (5, "sanei_pa4s2_readbyte: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pa4s2_readend (int fd)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_readend: called for fd %d\n", fd);
+ DBG (2, "sanei_pa4s2_readend: fd %d is invalid\n", fd);
+ DBG (3, "sanei_pa4s2_readend: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_readend: it's too late anyway\n");
+ DBG (5, "sanei_pa4s2_readend: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+}
+
+SANE_Status
+sanei_pa4s2_writebyte (int fd, u_char reg, u_char val)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_writebyte: called for fd %d and register %d, "
+ "value = %u\n", fd, (int) reg, (int) val);
+ DBG (2, "sanei_pa4s2_writebyte: fd %d is invalid\n", fd);
+ DBG (3, "sanei_pa4s2_writebyte: A4S2 support not compiled\n");
+ DBG (6, "sanei_pa4s2_writebyte: whatever backend you're using, tell\n");
+ DBG (6, "sanei_pa4s2_writebyte: the maintainer his code has bugs...\n");
+ DBG (5, "sanei_pa4s2_writebyte: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+}
+
+SANE_Status
+sanei_pa4s2_options (u_int * options, int set)
+{
+
+ TEST_DBG_INIT ();
+
+ DBG (4, "sanei_pa4s2_options: called with options %u and set = %d\n",
+ *options, set);
+
+ if ((set != SANE_TRUE) && (set != SANE_FALSE))
+ DBG (2, "sanei_pa4s2_options: value of set is invalid\n");
+
+ if ((set == SANE_TRUE) && (*options > 3))
+ DBG (2, "sanei_pa4s2_options: value of *options is invalid\n");
+
+ DBG (3, "sanei_pa4s2_options: A4S2 support not compiled\n");
+ DBG (5, "sanei_pa4s2_options: returning SANE_STATUS_INVAL\n");
+
+ return SANE_STATUS_INVAL;
+
+}
+
+const char **
+sanei_pa4s2_devices()
+{
+ TEST_DBG_INIT ();
+ DBG (4, "sanei_pa4s2_devices: invoked\n");
+
+ DBG (3, "sanei_pa4s2_devices: A4S2 support not compiled\n");
+ DBG (5, "sanei_pa4s2_devices: returning empty list\n");
+
+ return calloc(1, sizeof(char *));
+}
+
+SANE_Status
+sanei_pa4s2_scsi_pp_get_status(int fd, u_char *status)
+{
+ TEST_DBG_INIT ();
+ DBG (4, "sanei_pa4s2_scsi_pp_get_status: fd=%d, status=%p\n",
+ fd, (void *) status);
+ DBG (3, "sanei_pa4s2_scsi_pp_get_status: A4S2 support not compiled\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sanei_pa4s2_scsi_pp_reg_select (int fd, int reg)
+{
+ TEST_DBG_INIT ();
+ DBG (4, "sanei_pa4s2_scsi_pp_reg_select: fd=%d, reg=%d\n",
+ fd, reg);
+ DBG (3, "sanei_pa4s2_devices: A4S2 support not compiled\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sanei_pa4s2_scsi_pp_open (const char *dev, int *fd)
+{
+ TEST_DBG_INIT ();
+ DBG (4, "sanei_pa4s2_scsi_pp_open: dev=%s, fd=%p\n",
+ dev, (void *) fd);
+ DBG (3, "sanei_pa4s2_scsi_pp_open: A4S2 support not compiled\n");
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+#endif /* !HAVE_IOPERM */
diff --git a/sanei/sanei_pio.c b/sanei/sanei_pio.c
new file mode 100644
index 0000000..ef00861
--- /dev/null
+++ b/sanei/sanei_pio.c
@@ -0,0 +1,604 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998 Christian Bucher
+ Copyright (C) 1998 Kling & Hautzinger GmbH
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file implements the bi-directional parallel-port
+ interface. */
+
+/*
+ RESTRICTIONS:
+
+ - This interface is very timing sensitive, be carefull with setting
+ debug levels.
+ */
+
+#include "../include/sane/config.h"
+
+#define BACKEND_NAME sanei_pio
+#include "../include/sane/sanei_backend.h" /* pick up compatibility defs */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_IO_H
+# include <sys/io.h> /* use where available (glibc 2.x, for example) */
+#elif HAVE_ASM_IO_H
+# include <asm/io.h> /* ugly, but backwards compatible */
+#elif HAVE_SYS_HW_H
+# include <sys/hw.h>
+#elif defined(__i386__) && defined (__GNUC__)
+
+static __inline__ void
+outb (u_char value, u_long port)
+{
+ __asm__ __volatile__ ("outb %0,%1"::"a" (value), "d" ((u_short) port));
+}
+
+static __inline__ u_char
+inb (u_long port)
+{
+ u_char value;
+
+ __asm__ __volatile__ ("inb %1,%0":"=a" (value):"d" ((u_short) port));
+ return value;
+}
+
+#else
+# define IO_SUPPORT_MISSING
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_pio.h"
+
+#if defined (HAVE_IOPERM) && !defined (IO_SUPPORT_MISSING)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "../include/sane/saneopts.h"
+
+#define PORT_DEV "/dev/port"
+
+/* base 278 (lpt2)
+
+ ioport stat ctrl
+ offs 0 1 2
+ len 1 1 1 */
+
+/* Port definitions (`N' at end begin of label means negated signal) */
+
+#define PIO_IOPORT 0 /* rel. addr io port */
+
+#define PIO_STAT 1 /* rel. addr status port */
+#define PIO_STAT_BUSY (1<<7) /* BUSY Pin */
+#define PIO_STAT_NACKNLG (1<<6) /* ~ACKNLG Pin */
+
+#define PIO_CTRL 2 /* rel. addr control port */
+#define PIO_CTRL_IE (1<<5) /* Input enable */
+#define PIO_CTRL_IRQE (1<<4) /* enable IRQ */
+#define PIO_CTRL_DIR (1<<3) /* DIR pin, DIR=1 => out */
+#define PIO_CTRL_NINIT (1<<2) /* reset output */
+#define PIO_CTRL_FDXT (1<<1) /* Paper FEED (unused) */
+#define PIO_CTRL_NSTROBE (1<<0) /* strobe pin */
+
+#define PIO_APPLYRESET 2000 /* reset in 10us at init time */
+
+#define DL40 6
+#define DL50 7
+#define DL60 8
+#define DL61 9
+#define DL70 10
+#define DL71 11
+
+#ifdef NDEBUG
+# define DBG_INIT()
+#endif
+
+typedef struct
+ {
+ u_long base; /* i/o base address */
+ int fd; /* >= 0 when using /dev/port */
+ int max_time_seconds;/* forever if <= 0 */
+ u_int in_use; /* port in use? */
+ }
+PortRec, *Port;
+
+static PortRec port[] =
+ {
+ {0x378, -1, 0, 0},
+ {0x278, -1, 0, 0}
+ };
+
+extern int setuid (uid_t);
+
+static inline int pio_outb (const Port port, u_char val, u_long addr);
+static inline int pio_inb (const Port port, u_char * val, u_long addr);
+static inline int pio_wait (const Port port, u_char val, u_char mask);
+static inline void pio_ctrl (const Port port, u_char val);
+static inline void pio_delay (const Port port);
+static inline void pio_init (const Port port);
+static void pio_reset (const Port port);
+static int pio_write (const Port port, const u_char * buf, int n);
+static int pio_read (const Port port, u_char * buf, int n);
+static int pio_open (const char *dev, SANE_Status * status);
+
+static inline int
+pio_outb (const Port port, u_char val, u_long addr)
+{
+
+ if (-1 == port->fd)
+ outb (val, addr);
+ else
+ {
+ if (addr != (u_long)lseek (port->fd, addr, SEEK_SET))
+ return -1;
+ if (1 != write (port->fd, &val, 1))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int
+pio_inb (const Port port, u_char * val, u_long addr)
+{
+
+ if (-1 == port->fd)
+ *val = inb (addr);
+ else
+ {
+ if (addr != (u_long)lseek (port->fd, addr, SEEK_SET))
+ return -1;
+ if (1 != read (port->fd, val, 1))
+ return -1;
+ }
+ return 0;
+}
+
+static inline int
+pio_wait (const Port port, u_char val, u_char mask)
+{
+ int stat = 0;
+ long poll_count = 0;
+ time_t start = time(NULL);
+
+ DBG (DL60, "wait on port 0x%03lx for %02x mask %02x\n",
+ port->base, (int) val, (int) mask);
+ DBG (DL61, " BUSY %s\n", (mask & PIO_STAT_BUSY) ?
+ (val & PIO_STAT_BUSY ? "on" : "off") : "-");
+ DBG (DL61, " NACKNLG %s\n",
+ (mask & PIO_STAT_NACKNLG) ? (val & PIO_STAT_NACKNLG ? "on" : "off")
+ : "-");
+ for (;;)
+ {
+ ++poll_count;
+ stat = inb (port->base + PIO_STAT);
+ if ((stat & mask) == (val & mask))
+ {
+ DBG (DL60, "got %02x after %ld tries\n", stat, poll_count);
+ DBG (DL61, " BUSY %s\n", stat & PIO_STAT_BUSY ? "on" : "off");
+ DBG (DL61, " NACKNLG %s\n",
+ stat & PIO_STAT_NACKNLG ? "on" : "off");
+
+ return stat;
+ }
+ if(poll_count>1000)
+ {
+ if ((port->max_time_seconds>0) && (time(NULL)-start >= port->max_time_seconds))
+ break;
+ usleep(1);
+ }
+
+ }
+ DBG (DL60, "got %02x aborting after %ld\n", stat, poll_count);
+ DBG (DL61, " BUSY %s\n", stat & PIO_STAT_BUSY ? "on" : "off");
+ DBG (DL61, " NACKNLG %s\n", stat & PIO_STAT_NACKNLG ? "on" : "off");
+ DBG (1, "polling time out, abort\n");
+ exit (-1);
+}
+
+static inline void
+pio_ctrl (const Port port, u_char val)
+{
+ DBG (DL60, "ctrl on port 0x%03lx %02x %02x\n",
+ port->base, (int) val, (int) val ^ PIO_CTRL_NINIT);
+
+ val ^= PIO_CTRL_NINIT;
+
+ DBG (DL61, " IE %s\n", val & PIO_CTRL_IE ? "on" : "off");
+ DBG (DL61, " IRQE %s\n", val & PIO_CTRL_IRQE ? "on" : "off");
+ DBG (DL61, " DIR %s\n", val & PIO_CTRL_DIR ? "on" : "off");
+ DBG (DL61, " NINIT %s\n", val & PIO_CTRL_NINIT ? "on" : "off");
+ DBG (DL61, " FDXT %s\n", val & PIO_CTRL_FDXT ? "on" : "off");
+ DBG (DL61, " NSTROBE %s\n", val & PIO_CTRL_NSTROBE ? "on" : "off");
+
+ outb (val, port->base + PIO_CTRL);
+
+ return;
+}
+
+static inline void
+pio_delay (const Port port)
+{
+ inb (port->base + PIO_STAT); /* delay */
+
+ return;
+}
+
+static inline void
+pio_init (const Port port)
+{
+ pio_ctrl (port, PIO_CTRL_IE);
+ return;
+}
+
+static void
+pio_reset (const Port port)
+{
+ int n;
+
+ DBG (DL40, "reset\n");
+
+ for (n = PIO_APPLYRESET; --n >= 0;)
+ {
+ outb ((PIO_CTRL_IE | PIO_CTRL_NINIT) ^ PIO_CTRL_NINIT,
+ port->base + PIO_CTRL);
+ }
+ pio_init (port);
+
+ DBG (DL40, "end reset\n");
+
+ return;
+}
+
+static int
+pio_write (const Port port, const u_char * buf, int n)
+{
+ int k;
+
+ DBG (DL40, "write\n");
+
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+ pio_ctrl (port, PIO_CTRL_DIR | PIO_CTRL_IE); /* praeoutput */
+ pio_wait (port, PIO_STAT_NACKNLG, PIO_STAT_NACKNLG); /* acknlg */
+ pio_ctrl (port, PIO_CTRL_DIR); /* output */
+
+ for (k = 0; k < n; k++, buf++)
+ {
+ DBG (DL40, "write byte\n");
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY | PIO_STAT_NACKNLG,
+ PIO_STAT_BUSY | PIO_STAT_NACKNLG); /* busyack */
+#endif
+ DBG (DL60, "out %02x\n", (int) *buf);
+
+ outb (*buf, port->base + PIO_IOPORT);
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+ pio_ctrl (port, PIO_CTRL_DIR | PIO_CTRL_NSTROBE); /* outputstrobe */
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+ pio_ctrl (port, PIO_CTRL_DIR); /* output */
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+
+ DBG (DL40, "end write byte\n");
+ }
+
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY | PIO_STAT_NACKNLG,
+ PIO_STAT_BUSY | PIO_STAT_NACKNLG); /* busyack */
+#endif
+
+ pio_ctrl (port, PIO_CTRL_DIR | PIO_CTRL_IE); /* praeoutput */
+ DBG (DL40, "end write\n");
+ return k;
+}
+
+static int
+pio_read (const Port port, u_char * buf, int n)
+{
+ int k;
+
+ DBG (DL40, "read\n");
+
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+ pio_ctrl (port, PIO_CTRL_IE); /* input */
+
+ for (k = 0; k < n; k++, buf++)
+ {
+ DBG (DL40, "read byte\n");
+
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY | PIO_STAT_NACKNLG);
+ /* busynack */
+#endif
+ pio_ctrl (port, PIO_CTRL_IE | PIO_CTRL_NSTROBE); /* inputstrobe */
+
+ pio_delay (port);
+ pio_delay (port);
+ pio_delay (port);
+ pio_ctrl (port, PIO_CTRL_IE); /* input */
+#ifdef HANDSHAKE_BUSY
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+#else
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY | PIO_STAT_NACKNLG);
+ /* busynack */
+#endif
+
+ *buf = inb (port->base + PIO_IOPORT);
+ DBG (DL60, "in %02x\n", (int) *buf);
+ DBG (DL40, "end read byte\n");
+ }
+
+ pio_wait (port, PIO_STAT_BUSY, PIO_STAT_BUSY); /* busy */
+ pio_ctrl (port, PIO_CTRL_IE); /* input */
+ DBG (DL40, "end read\n");
+ return k;
+}
+
+/*
+ Open the device, <dev> must contain a valid port number (as string).
+ */
+
+static int
+pio_open (const char *dev, SANE_Status * status)
+{
+ static int first_time = 1;
+ u_long base;
+ int n;
+
+ if (first_time)
+ {
+ first_time = 0;
+
+ DBG_INIT ();
+ /* set root uid */
+ if (0 > setuid (0))
+ {
+ DBG (1, "sanei_pio_open: setuid failed: errno = %d\n", errno);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+ }
+ /* read port number */
+ {
+ char *end;
+
+ base = strtol (dev, &end, 0);
+
+ if ((end == dev) || *end)
+ {
+ DBG (1, "sanei_pio_open: `%s' is not a valid port number\n", dev);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+ }
+
+ if (0 == base)
+ {
+ DBG (1, "sanei_pio_open: 0x%03lx is not a valid base address\n", base);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ for (n = 0; n < NELEMS (port); n++)
+ if (port[n].base == base)
+ break;
+
+ if (NELEMS (port) <= n)
+ {
+ DBG (1, "sanei_pio_open: 0x%03lx is not a valid base address\n", base);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+ if (port[n].in_use)
+ {
+ DBG (1, "sanei_pio_open: port 0x%03lx is already in use\n", base);
+ *status = SANE_STATUS_DEVICE_BUSY;
+ return -1;
+ }
+ port[n].base = base;
+ port[n].fd = -1;
+ port[n].max_time_seconds = 10;
+ port[n].in_use = 1;
+
+ if (ioperm (port[n].base, 3, 1))
+ {
+ DBG (1, "sanei_pio_open: cannot get io privilege for port 0x%03lx\n",
+ port[n].base);
+ *status = SANE_STATUS_IO_ERROR;
+ return -1;
+ }
+
+ pio_reset (&port[n]);
+
+ *status = SANE_STATUS_GOOD;
+ return n;
+}
+
+SANE_Status
+sanei_pio_open (const char *dev, int *fdp)
+{
+ SANE_Status status;
+
+ *fdp = pio_open (dev, &status);
+ return status;
+}
+
+void
+sanei_pio_close (int fd)
+{
+ Port p = port + fd;
+
+ if ((0 > fd) && (NELEMS (port) <= fd))
+ return;
+
+ if (!p->in_use)
+ return;
+
+ if (-1 != p->fd)
+ {
+ close (p->fd);
+ p->fd = -1;
+ }
+
+ p->in_use = 0;
+
+ return;
+}
+
+int
+sanei_pio_read (int fd, u_char * buf, int n)
+{
+ if ((0 > fd) && (NELEMS (port) <= fd))
+ return -1;
+
+ if (!port[fd].in_use)
+ return -1;
+
+ return pio_read (&port[fd], buf, n);
+}
+
+int
+sanei_pio_write (int fd, const u_char * buf, int n)
+{
+ if ((0 > fd) && (NELEMS (port) <= fd))
+ return -1;
+
+ if (!port[fd].in_use)
+ return -1;
+
+ return pio_write (&port[fd], buf, n);
+}
+
+#else /* !HAVE_IOPERM */
+
+#ifdef __BEOS__
+
+#include <fcntl.h>
+
+SANE_Status
+sanei_pio_open (const char *dev, int *fdp)
+{
+ int fp;
+
+ /* open internal parallel port */
+ fp=open("/dev/parallel/parallel1",O_RDWR);
+
+ *fdp=fp;
+ if(fp<0) return SANE_STATUS_INVAL;
+ return(SANE_STATUS_GOOD);
+}
+
+
+void
+sanei_pio_close (int fd)
+{
+ close(fd);
+ return;
+}
+
+int
+sanei_pio_read (int fd, u_char * buf, int n)
+{
+ return(read(fd,buf,n));
+}
+
+int
+sanei_pio_write (int fd, const u_char * buf, int n)
+{
+ return(write(fd,buf,n));
+}
+
+#else /* !__BEOS__ */
+
+SANE_Status
+sanei_pio_open (const char *dev, int *fdp)
+{
+ *fdp = -1;
+ return SANE_STATUS_INVAL;
+}
+
+
+void
+sanei_pio_close (int fd)
+{
+ return;
+}
+
+int
+sanei_pio_read (int fd, u_char * buf, int n)
+{
+ return -1;
+}
+
+int
+sanei_pio_write (int fd, const u_char * buf, int n)
+{
+ return -1;
+}
+#endif /* __BEOS__ */
+
+#endif /* !HAVE_IOPERM */
diff --git a/sanei/sanei_pp.c b/sanei/sanei_pp.c
new file mode 100644
index 0000000..ced1a85
--- /dev/null
+++ b/sanei/sanei_pp.c
@@ -0,0 +1,1462 @@
+/* sane - Scanner Access Now Easy.
+ * Copyright (C) 2003-2005 Gerhard Jaeger <gerhard@gjaeger.de>
+ * based on work done by Jochen Eisinger <jochen.eisinger@gmx.net>
+ * also parts from libieee1284 by Tim Waugh <tim@cyberelk.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * As a special exception, the authors of SANE give permission for
+ * additional uses of the libraries contained in this release of SANE.
+ *
+ * The exception is that, if you link a SANE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public
+ * License. Your use of that executable is in no way restricted on
+ * account of linking the SANE library code into it.
+ *
+ * This exception does not, however, invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public
+ * License.
+ *
+ * If you submit changes to SANE to the maintainers to be included in
+ * a subsequent release, you agree by submitting the changes that
+ * those changes may be distributed with this exception intact.
+ *
+ * If you write modifications of your own for SANE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ *
+ * This file implements an interface for accessing the parallelport
+ */
+
+/* debug levels:
+ * 0 - nothing
+ * 1 - errors
+ * 2 - warnings
+ * 3 - things nice to know
+ * 4 - code flow
+ * 5 - detailed flow
+ * 6 - everything
+ *
+ * These debug levels can be set using the environment variable
+ * SANE_DEBUG_SANEI_PP
+ */
+
+#include "../include/sane/config.h"
+
+#define BACKEND_NAME sanei_pp
+
+#define _TEST_LOOPS 1000
+#define _MAX_PORTS 20
+
+#ifndef _VAR_NOT_USED
+# define _VAR_NOT_USED(x) ((x)=(x))
+#endif
+
+/* uncomment this to have some parameter checks on in/out functions,
+ * note: that this will slow down the calls
+ */
+#if 0
+# define _PARANOIA
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#else
+# ifndef ULONG_MAX
+# define ULONG_MAX 4294967295UL
+# endif
+#endif
+#if defined (ENABLE_PARPORT_DIRECTIO)
+# undef HAVE_LIBIEEE1284
+# if defined(HAVE_SYS_IO_H)
+# if defined (__ICC) && __ICC >= 700
+# define __GNUC__ 2
+# endif
+# include <sys/io.h>
+# if defined (__ICC) && __ICC >= 700
+# undef __GNUC__
+# elif defined(__ICC) && defined(HAVE_ASM_IO_H)
+# include <asm/io.h>
+# endif
+# elif defined(HAVE_ASM_IO_H)
+# include <asm/io.h>
+# elif defined(HAVE_SYS_HW_H)
+# include <sys/hw.h>
+# elif defined(__i386__) && ( defined (__GNUC__) || defined (__ICC) )
+
+static __inline__ void
+outb( u_char value, u_long port )
+{
+ __asm__ __volatile__ ("outb %0,%1"::"a" (value), "d" ((u_short) port));
+}
+
+static __inline__ u_char
+inb( u_long port )
+{
+ u_char value;
+
+ __asm__ __volatile__ ("inb %1,%0":"=a" (value):"d" ((u_short) port));
+ return value;
+}
+# endif
+#elif defined(HAVE_LIBIEEE1284)
+# include <ieee1284.h>
+#else
+# if defined(__GNUC__)
+# warning "No I/O support for this architecture!"
+# endif
+# define IO_SUPPORT_MISSING
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_pp.h"
+
+#if defined(STDC_HEADERS)
+# include <errno.h>
+# include <stdio.h>
+# include <stdlib.h>
+#endif
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#elif defined(HAVE_STRINGS_H)
+# include <strings.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+
+/** our global init flag... */
+static int first_time = SANE_TRUE;
+static unsigned long pp_thresh = 0;
+
+#if (defined (HAVE_IOPERM) || defined (HAVE_LIBIEEE1284)) && !defined (IO_SUPPORT_MISSING)
+
+typedef struct {
+
+#ifndef HAVE_LIBIEEE1284
+ const char name[6];
+ u_long base; /**< i/o base address */
+ u_char ctrl; /**< for restoring CTRL register */
+ u_char ecp_ctrl; /**< for restoring ECP-CTRL register */
+#endif
+
+ u_int in_use; /**< port in use? */
+ u_int claimed; /**< port claimed? */
+
+ int caps; /**< port capabilities */
+
+} PortRec, *Port;
+
+#if defined (HAVE_LIBIEEE1284)
+
+static struct parport_list pplist;
+static PortRec port[_MAX_PORTS];
+
+#else
+
+/** redefine the CAPability flags */
+enum ieee1284_capabilities
+{
+ CAP1284_RAW = (1<<0),
+ CAP1284_NIBBLE = (1<<1), /* SPP mode */
+ CAP1284_BYTE = (1<<2), /* PS/2 bidirectional */
+ CAP1284_COMPAT = (1<<3),
+ CAP1284_BECP = (1<<4),
+ CAP1284_ECP = (1<<5), /* ECP */
+ CAP1284_ECPRLE = (1<<6), /* ECP with RLE support */
+ CAP1284_ECPSWE = (1<<7),
+ CAP1284_EPP = (1<<8), /* EPP hardware support */
+ CAP1284_EPPSL = (1<<9), /* EPP 1.7 */
+ CAP1284_EPPSWE = (1<<10) /* EPP software support */
+};
+
+static PortRec port[] = {
+ { "0x378", 0x378, 0, 0, SANE_FALSE, SANE_FALSE, 0 },
+ { "0x278", 0x278, 0, 0, SANE_FALSE, SANE_FALSE, 0 },
+ { "0x3BC", 0x3BC, 0, 0, SANE_FALSE, SANE_FALSE, 0 }
+};
+
+#endif
+
+/** depending on the interface we use, define the port macros
+ */
+#if defined(HAVE_LIBIEEE1284)
+
+#define inb_data(fd) ieee1284_read_data(pplist.portv[fd]);
+#define inb_stat(fd) (ieee1284_read_status(pplist.portv[fd]) ^ S1284_INVERTED)
+#define inb_ctrl(fd) (ieee1284_read_control(pplist.portv[fd]) ^ C1284_INVERTED)
+
+static inline u_char inb_eppdata(int fd)
+{
+ u_char val;
+ ieee1284_epp_read_data(pplist.portv[fd], 0, (char *)&val, 1);
+ return val;
+}
+
+static inline void outb_eppdata(int fd, u_char val)
+{
+ ieee1284_epp_write_data(pplist.portv[fd], 0, (const char *)&val, 1);
+}
+
+#define outb_data(fd,val) ieee1284_write_data(pplist.portv[fd], val)
+#define outb_ctrl(fd,val) ieee1284_write_control(pplist.portv[fd], \
+ (val) ^ C1284_INVERTED)
+static inline void outb_addr(int fd, u_char val)
+{
+ ieee1284_epp_write_addr (pplist.portv[fd], 0, (char *)&val, 1);
+}
+
+#else
+
+#define inb_data(fd) inb(port[fd].base)
+#define inb_stat(fd) inb(port[fd].base + 1)
+#define inb_ctrl(fd) inb(port[fd].base + 2)
+#define inb_eppdata(fd) inb(port[fd].base + 4)
+
+#define outb_data(fd,val) outb(val, port[fd].base)
+#define outb_stat(fd,val) outb(val, port[fd].base + 1)
+#define outb_ctrl(fd,val) outb(val, port[fd].base + 2)
+#define outb_addr(fd,val) outb(val, port[fd].base + 3)
+#define outb_eppdata(fd,val) outb(val, port[fd].base + 4)
+
+#ifdef HAVE_IOPL
+# define _SET_IOPL() iopl(3)
+# define inbyte400(fd) inb(port[fd].base + 0x400)
+# define inbyte402(fd) inb(port[fd].base + 0x402)
+# define outbyte400(fd,val) outb(val, port[fd].base + 0x400)
+# define outbyte402(fd,val) outb(val, port[fd].base + 0x402)
+#else
+# define _SET_IOPL()
+# define inbyte400(fd)
+# define inbyte402(fd,val)
+# define outbyte400(fd,val)
+# define outbyte402(fd,val)
+#endif
+#endif
+
+/* should also be in unistd.h */
+extern int setuid (uid_t);
+
+#if defined (HAVE_LIBIEEE1284)
+
+static const char *pp_libieee1284_errorstr( int error )
+{
+ switch (error) {
+
+ case E1284_OK:
+ return "Everything went fine";
+
+ case E1284_NOTIMPL:
+ return "Not implemented in libieee1284";
+
+ case E1284_NOTAVAIL:
+ return "Not available on this system";
+
+ case E1284_TIMEDOUT:
+ return "Operation timed out";
+
+ case E1284_REJECTED:
+ return "IEEE 1284 negotiation rejected";
+
+ case E1284_NEGFAILED:
+ return "Negotiation went wrong";
+
+ case E1284_NOMEM:
+ return "No memory left";
+
+ case E1284_INIT:
+ return "Error initializing port";
+
+ case E1284_SYS:
+ return "Error interfacing system";
+
+ case E1284_NOID:
+ return "No IEEE 1284 ID available";
+
+ case E1284_INVALIDPORT:
+ return "Invalid port";
+
+ default:
+ return "Unknown error";
+ }
+}
+#endif
+
+/** show the caps
+ */
+static int
+pp_showcaps( int caps )
+{
+ int mode = 0;
+ char ct[1024];
+
+ ct[0] = '\0';
+
+ if( caps & CAP1284_NIBBLE ) {
+ strcat( ct, "SPP " );
+ mode |= SANEI_PP_MODE_SPP;
+ }
+
+ if( caps & CAP1284_BYTE ) {
+ strcat( ct, "PS/2 " );
+ mode |= SANEI_PP_MODE_BIDI;
+ }
+
+ if( caps & CAP1284_EPP ) {
+ strcat( ct, "EPP " );
+ mode |= SANEI_PP_MODE_EPP;
+ }
+
+ if( caps & CAP1284_EPPSWE ) {
+ strcat( ct, "EPPSWE " );
+ mode |= SANEI_PP_MODE_EPP;
+ }
+
+ if( caps & CAP1284_ECP ) {
+ strcat( ct, "ECP " );
+ mode |= SANEI_PP_MODE_ECP;
+ }
+
+ if( caps & CAP1284_ECPRLE ) {
+ strcat( ct, "ECPRLE " );
+ mode |= SANEI_PP_MODE_ECP;
+ }
+
+ DBG( 4, "Supported Modes: %s\n", ct );
+ return mode;
+}
+
+#ifndef HAVE_LIBIEEE1284
+
+/** probe the parallel port
+ */
+static int
+pp_probe( int fd )
+{
+#ifdef HAVE_IOPL
+ SANE_Byte c;
+ int i, j;
+#endif
+ SANE_Byte a, b;
+ int retv = 0;
+
+ DBG( 4, "pp_probe: port 0x%04lx\n", port[fd].base );
+
+ /* SPP check */
+ outbyte402( fd, 0x0c );
+ outb_ctrl ( fd, 0x0c );
+ outb_data ( fd, 0x55 );
+ a = inb_data( fd );
+ if( a != 0x55 ) {
+ DBG( 4, "pp_probe: nothing supported :-(\n" );
+ return retv;
+ }
+
+ DBG( 4, "pp_probe: SPP port present\n" );
+ retv += CAP1284_NIBBLE;
+
+ /* check for ECP */
+#ifdef HAVE_IOPL
+
+ /* clear at most 1k of data from FIFO */
+ for( i = 1024; i > 0; i-- ) {
+ a = inbyte402( fd );
+ if ((a & 0x03) == 0x03)
+ goto no_ecp;
+ if (a & 0x01)
+ break;
+ inbyte400( fd ); /* Remove byte from FIFO */
+ }
+
+ if (i <= 0)
+ goto no_ecp;
+
+ b = a ^ 3;
+ outbyte402( fd, b );
+ c = inbyte402( fd );
+
+ if (a == c) {
+ outbyte402( fd, 0xc0 ); /* FIFO test */
+ j = 0;
+ while (!(inbyte402( fd ) & 0x01) && (j < 1024)) {
+ inbyte402( fd );
+ j++;
+ }
+ if (j >= 1024)
+ goto no_ecp;
+ i = 0;
+ j = 0;
+ while (!(inbyte402( fd ) & 0x02) && (j < 1024)) {
+ outbyte400( fd, 0x00 );
+ i++;
+ j++;
+ }
+ if (j >= 1024)
+ goto no_ecp;
+ j = 0;
+ while (!(inbyte402( fd ) & 0x01) && (j < 1024)) {
+ inbyte400( fd );
+ j++;
+ }
+ if (j >= 1024)
+ goto no_ecp;
+
+ DBG( 4, "pp_probe: ECP with a %i byte FIFO present\n", i );
+ retv += CAP1284_ECP;
+ }
+
+no_ecp:
+#endif
+ /* check for PS/2 compatible port */
+ if( retv & CAP1284_ECP ) {
+ outbyte402( fd, 0x20 );
+ }
+
+ outb_data( fd, 0x55 );
+ outb_ctrl( fd, 0x0c );
+ a = inb_data( fd );
+ outb_data( fd, 0x55 );
+ outb_ctrl( fd, 0x2c );
+ b = inb_data( fd );
+ if( a != b ) {
+ DBG( 4, "pp_probe: PS/2 bidirectional port present\n");
+ retv += CAP1284_BYTE;
+ }
+
+ /* check for EPP support */
+ if( port[fd].base & 0x007 ) {
+ DBG( 4, "pp_probe: EPP not supported at this address\n" );
+ return retv;
+ }
+#ifdef HAVE_IOPL
+ if( retv & CAP1284_ECP ) {
+ for( i = 0x00; i < 0x80; i += 0x20 ) {
+ outbyte402( fd, i );
+
+ a = inb_stat( fd );
+ outb_stat( fd, a );
+ outb_stat( fd, (a & 0xfe));
+ a = inb_stat( fd );
+ if (!(a & 0x01)) {
+ DBG( 2, "pp_probe: "
+ "Failed Intel bug check. (Phony EPP in ECP)\n" );
+ return retv;
+ }
+ }
+ DBG( 4, "pp_probe: Passed Intel bug check.\n" );
+ outbyte402( fd, 0x80 );
+ }
+#endif
+
+ a = inb_stat( fd );
+ outb_stat( fd, a );
+ outb_stat( fd, (a & 0xfe));
+ a = inb_stat( fd );
+
+ if (a & 0x01) {
+ outbyte402( fd, 0x0c );
+ outb_ctrl ( fd, 0x0c );
+ return retv;
+ }
+
+ outb_ctrl( fd, 0x04 );
+ inb_eppdata ( fd );
+ a = inb_stat( fd );
+ outb_stat( fd, a );
+ outb_stat( fd, (a & 0xfe));
+
+ if( a & 0x01 ) {
+ DBG( 4, "pp_probe: EPP 1.9 with hardware direction protocol\n");
+ retv += CAP1284_EPP;
+ } else {
+ /* The EPP timeout bit was not set, this could either be:
+ * EPP 1.7
+ * EPP 1.9 with software direction
+ */
+ outb_ctrl( fd, 0x24 );
+ inb_eppdata ( fd );
+ a = inb_stat( fd );
+ outb_stat( fd, a );
+ outb_stat( fd, (a & 0xfe));
+ if( a & 0x01 ) {
+ DBG( 4, "pp_probe: EPP 1.9 with software direction protocol\n" );
+ retv += CAP1284_EPPSWE;
+ } else {
+ DBG( 4, "pp_probe: EPP 1.7\n" );
+ retv += CAP1284_EPPSL;
+ }
+ }
+
+ outbyte402( fd, 0x0c );
+ outb_ctrl ( fd, 0x0c );
+ return retv;
+}
+
+/**
+ */
+static int pp_set_scpmode( int fd )
+{
+ SANE_Byte tmp;
+ DBG( 4, "pp_set_scpmode\n" );
+
+#ifdef HAVE_IOPL
+ tmp = inbyte402( fd );
+ tmp &= 0x1f;
+ outbyte402( fd, tmp );
+#endif
+ tmp = inb_ctrl( fd );
+ tmp &= 0x0f;
+ outb_ctrl ( fd, tmp );
+
+ return SANE_STATUS_GOOD;
+}
+
+static int pp_set_bidimode( int fd )
+{
+ SANE_Byte tmp;
+ DBG( 4, "pp_set_bidimode\n" );
+#ifdef HAVE_IOPL
+ tmp = inbyte402( fd );
+ tmp = (tmp & 0x1f) | 0x20;
+ outbyte402( fd, tmp );
+#endif
+ tmp = inb_ctrl( fd );
+ tmp = (tmp & 0x0f) | 0x20;
+ outb_ctrl ( fd, tmp );
+
+ return SANE_STATUS_GOOD;
+}
+
+static int pp_set_eppmode( int fd )
+{
+ SANE_Byte tmp;
+ DBG( 4, "pp_set_eppmode\n" );
+#ifdef HAVE_IOPL
+ tmp = inbyte402( fd );
+ tmp = (tmp & 0x1f) | 0x80;
+ outbyte402( fd, tmp );
+#endif
+ tmp = inb_ctrl( fd );
+ tmp = (tmp & 0xf0) | 0x40;
+ outb_ctrl ( fd, tmp );
+
+ return SANE_STATUS_GOOD;
+}
+
+static int pp_set_ecpmode( int fd )
+{
+#ifdef HAVE_IOPL
+ SANE_Byte tmp;
+#endif
+
+ DBG( 4, "pp_set_ecpmode\n" );
+#ifdef HAVE_IOPL
+ tmp = inbyte402( fd );
+ tmp = (tmp & 0x1f) | 0x60;
+ outbyte402( fd, tmp );
+ return SANE_STATUS_GOOD;
+#endif
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/** set the parallel port mode
+ */
+static int
+pp_setmode( int fd, int mode )
+{
+ int ret;
+
+ if( 0 == (mode & port[fd].caps)) {
+ DBG( 2, "pp_setmode: mode not supported %d\n", mode );
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ switch( mode ) {
+ case SANEI_PP_MODE_SPP: ret = pp_set_scpmode( fd ); break;
+ case SANEI_PP_MODE_BIDI: ret = pp_set_bidimode( fd ); break;
+ case SANEI_PP_MODE_EPP: ret = pp_set_eppmode( fd ); break;
+ case SANEI_PP_MODE_ECP: ret = pp_set_ecpmode( fd ); break;
+
+ default:
+ DBG( 2, "pp_setmode: invalid mode %d\n", mode );
+ return SANE_STATUS_INVAL;
+ }
+
+ return ret;
+}
+
+#endif
+
+static unsigned long
+pp_time_diff( struct timeval *start, struct timeval *end )
+{
+ double s, e, r;
+
+ s = (double)start->tv_sec * 1000000.0 + (double)start->tv_usec;
+ e = (double)end->tv_sec * 1000000.0 + (double)end->tv_usec;
+
+ if( e > s )
+ r = (e - s);
+ else
+ r = (s - e);
+
+ if( r <= (double)ULONG_MAX )
+ return (unsigned long)r;
+
+ return 0;
+}
+
+/**
+ */
+static unsigned long
+pp_calculate_thresh( void )
+{
+ unsigned long i, r, ret;
+ struct timeval start, end, deadline;
+
+ gettimeofday( &start, NULL);
+
+ for( i = _TEST_LOOPS; i; i-- ) {
+
+ gettimeofday( &deadline, NULL );
+ deadline.tv_usec += 10;
+ deadline.tv_sec += deadline.tv_usec / 1000000;
+ deadline.tv_usec %= 1000000;
+ }
+
+ gettimeofday( &end, NULL);
+
+ r = pp_time_diff( &start, &end );
+ ret = r/_TEST_LOOPS;
+ return ret;
+}
+
+/**
+ */
+static void
+pp_calibrate_delay( void )
+{
+ unsigned long r, i;
+ struct timeval start, end;
+
+ for( i = 0; i < 5; i++ ) {
+
+ pp_thresh = pp_calculate_thresh();
+ gettimeofday( &start, NULL);
+
+ for( i = _TEST_LOOPS; i; i-- ) {
+ sanei_pp_udelay( 1 );
+ }
+ gettimeofday( &end, NULL);
+
+ r = pp_time_diff( &start, &end );
+
+ DBG( 4, "pp_calibrate_delay: Delay expected: "
+ "%u, real %lu, pp_thresh=%lu\n", _TEST_LOOPS, r, pp_thresh );
+
+ if( r >= _TEST_LOOPS ) {
+ return;
+ }
+ }
+
+ DBG( 4, "pp_calibrate_delay: pp_thresh set to 0\n" );
+ pp_thresh = 0;
+}
+
+static SANE_Status
+pp_init( void )
+{
+#if defined (HAVE_LIBIEEE1284)
+ int result, i;
+#endif
+
+ if( first_time == SANE_FALSE ) {
+ DBG( 5, "pp_init: already initalized\n" );
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG( 5, "pp_init: called for the first time\n");
+ first_time = SANE_FALSE;
+
+#if defined (HAVE_LIBIEEE1284)
+
+ DBG( 4, "pp_init: initializing libieee1284\n");
+ result = ieee1284_find_ports( &pplist, 0 );
+
+ if (result) {
+ DBG (1, "pp_init: initializing IEEE 1284 failed (%s)\n",
+ pp_libieee1284_errorstr( result ));
+ first_time = SANE_TRUE;
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG( 3, "pp_init: %d ports reported by IEEE 1284 library\n", pplist.portc);
+
+ for( i = 0; i < pplist.portc; i++ )
+ DBG( 6, "pp_init: port %d is `%s`\n", i, pplist.portv[i]->name);
+
+ /* we support only up to _MAX_PORTS... */
+ if( pplist.portc > _MAX_PORTS ) {
+ DBG (1, "pp_init: Lib IEEE 1284 reports too much ports: %d\n",
+ pplist.portc );
+
+ ieee1284_free_ports( &pplist );
+ first_time = SANE_TRUE;
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ memset( port, 0, sizeof(port));
+
+#else
+
+ DBG( 4, "pp_init: trying to setuid root\n");
+ if( 0 > setuid( 0 )) {
+
+ DBG( 1, "pp_init: setuid failed: errno = %d\n", errno );
+ DBG( 5, "pp_init: returning SANE_STATUS_INVAL\n" );
+
+ first_time = SANE_TRUE;
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG( 3, "pp_init: the application is now root\n" );
+
+#endif
+
+ DBG( 5, "pp_init: initialized successfully\n" );
+ return SANE_STATUS_GOOD;
+}
+
+static int
+pp_open( const char *dev, SANE_Status * status )
+{
+ int i;
+#if !defined (HAVE_LIBIEEE1284)
+ u_long base;
+#else
+ int result;
+#endif
+
+ DBG( 4, "pp_open: trying to attach dev `%s`\n", dev );
+
+#if !defined (HAVE_LIBIEEE1284)
+{
+ char *end;
+
+ DBG( 5, "pp_open: reading port number\n" );
+
+ base = strtol( dev, &end, 0 );
+ if ((end == dev) || (*end != '\0')) {
+
+ DBG( 1, "pp_open: `%s` is not a valid port number\n", dev);
+ DBG( 6, "pp_open: the part I did not understand was ...`%s`\n", end);
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+}
+
+ DBG( 6, "pp_open: read port number 0x%03lx\n", base );
+ if( base == 0 ) {
+
+ DBG( 1, "pp_open: 0x%03lx is not a valid base address\n", base );
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+#endif
+
+ DBG( 5, "pp_open: looking up port in list\n" );
+
+#if defined (HAVE_LIBIEEE1284)
+ for( i = 0; i < pplist.portc; i++ ) {
+ DBG( 5, "pp_open: checking >%s<\n", pplist.portv[i]->name );
+ if( !strcmp(pplist.portv[i]->name, dev))
+ break;
+ }
+
+ if( pplist.portc <= i ) {
+ DBG( 1, "pp_open: `%s` is not a valid device name\n", dev );
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+#else
+
+ for( i = 0; i < NELEMS(port); i++ ) {
+ if( port[i].base == base )
+ break;
+ }
+
+ if (NELEMS (port) <= i) {
+
+ DBG( 1, "pp_open: 0x%03lx is not a valid base address\n", base );
+ *status = SANE_STATUS_INVAL;
+ return -1;
+ }
+
+#endif
+
+ DBG( 6, "pp_open: port is in list at port[%d]\n", i);
+
+ if( port[i].in_use == SANE_TRUE) {
+
+#if defined (HAVE_LIBIEEE1284)
+ DBG( 1, "pp_open: device `%s` is already in use\n", dev );
+#else
+ DBG( 1, "pp_open: port 0x%03lx is already in use\n", base );
+#endif
+ *status = SANE_STATUS_DEVICE_BUSY;
+ return -1;
+ }
+
+ port[i].in_use = SANE_TRUE;
+ port[i].claimed = SANE_FALSE;
+
+#if defined (HAVE_LIBIEEE1284)
+
+ DBG( 5, "pp_open: opening device\n" );
+ result = ieee1284_open( pplist.portv[i], 0, &port[i].caps );
+ if (result) {
+ DBG( 1, "pp_open: could not open device `%s` (%s)\n",
+ dev, pp_libieee1284_errorstr (result));
+ port[i].in_use = SANE_FALSE;
+ *status = SANE_STATUS_ACCESS_DENIED;
+ return -1;
+ }
+
+#else
+
+ DBG( 5, "pp_open: getting io permissions\n" );
+
+ /* TODO: insert FreeBSD compatible code here */
+
+ if( ioperm( port[i].base, 5, 1 )) {
+ DBG( 1, "pp_open: cannot get io privilege for port 0x%03lx\n",
+ port[i].base);
+
+ port[i].in_use = SANE_FALSE;
+ *status = SANE_STATUS_IO_ERROR;
+ return -1;
+ }
+
+ /* save the CTRL register settings */
+#ifdef HAVE_IOPL
+ _SET_IOPL();
+ port[i].ecp_ctrl = inbyte402(i);
+ port[i].ctrl = inb_ctrl(i);
+#endif
+
+ /* check the capabilities of this port */
+ port[i].caps = pp_probe( i );
+#endif
+
+ port[i].caps = pp_showcaps( port[i].caps );
+ DBG( 3, "pp_open: device `%s` opened...\n", dev );
+ *status = SANE_STATUS_GOOD;
+ return i;
+}
+
+static int
+pp_close( int fd, SANE_Status *status )
+{
+#if defined(HAVE_LIBIEEE1284)
+ int result;
+#endif
+ DBG( 4, "pp_close: fd=%d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ DBG( 6, "pp_close: this is port '%s'\n", pplist.portv[fd]->name );
+#else
+ DBG( 6, "pp_close: this is port 0x%03lx\n", port[fd].base );
+ DBG( 6, "pp_close: restoring the CTRL registers\n" );
+ outb_ctrl( fd, port[fd].ctrl );
+#ifdef HAVE_IOPL
+ outbyte402( fd, port[fd].ecp_ctrl );
+#endif
+#endif
+
+ if( port[fd].claimed == SANE_TRUE ) {
+ sanei_pp_release( fd );
+ }
+
+ DBG( 5, "pp_close: trying to free io port\n" );
+#if defined(HAVE_LIBIEEE1284)
+ if((result = ieee1284_close(pplist.portv[fd])) < 0) {
+#else
+ if( ioperm( port[fd].base, 5, 0 )) {
+#endif
+#if defined(HAVE_LIBIEEE1284)
+ DBG( 1, "pp_close: can't free port '%s' (%s)\n",
+ pplist.portv[fd]->name, pp_libieee1284_errorstr(result));
+#else
+ DBG( 1, "pp_close: can't free port 0x%03lx\n", port[fd].base );
+#endif
+ *status = SANE_STATUS_IO_ERROR;
+ return -1;
+ }
+
+ DBG( 5, "pp_close: marking port as unused\n" );
+ port[fd].in_use = SANE_FALSE;
+
+ *status = SANE_STATUS_GOOD;
+ return 0;
+}
+
+/** exported functions **/
+
+SANE_Status
+sanei_pp_init( void )
+{
+ SANE_Status result;
+
+ DBG_INIT();
+
+ result = pp_init();
+ if( result != SANE_STATUS_GOOD ) {
+ return result;
+ }
+
+ pp_calibrate_delay();
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_open( const char *dev, int *fd )
+{
+ SANE_Status status;
+
+ DBG( 4, "sanei_pp_open: called for device '%s'\n", dev);
+
+ *fd = pp_open( dev, &status );
+ if( *fd == -1 ) {
+ DBG( 5, "sanei_pp_open: connection failed\n" );
+ return status;
+ }
+
+ DBG( 6, "sanei_pp_open: connected to device using fd %u\n", *fd );
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_pp_close( int fd )
+{
+ SANE_Status status;
+
+ DBG( 4, "sanei_pp_close: fd = %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_close: fd %d is invalid\n", fd );
+ return;
+ }
+
+ if( port[fd].in_use == SANE_FALSE ) {
+
+ DBG( 2, "sanei_pp_close: port is not in use\n" );
+#if defined(HAVE_LIBIEEE1284)
+ DBG( 6, "sanei_pp_close: port is '%s'\n", pplist.portv[fd]->name );
+#else
+ DBG( 6, "sanei_pp_close: port is 0x%03lx\n", port[fd].base );
+#endif
+ return;
+ }
+
+ DBG( 5, "sanei_pp_close: freeing resources\n" );
+ if( pp_close (fd, &status) == -1 ) {
+ DBG( 5, "sanei_pp_close: failed\n" );
+ return;
+ }
+ DBG( 5, "sanei_pp_close: finished\n" );
+}
+
+SANE_Status
+sanei_pp_claim( int fd )
+{
+#if defined (HAVE_LIBIEEE1284)
+ int result;
+#endif
+ DBG( 4, "sanei_pp_claim: fd = %d\n", fd );
+
+#if defined (HAVE_LIBIEEE1284)
+ if((fd < 0) || (fd >= pplist.portc)) {
+ DBG( 2, "sanei_pp_claim: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+
+ result = ieee1284_claim (pplist.portv[fd]);
+ if (result) {
+ DBG (1, "sanei_pp_claim: failed (%s)\n",
+ pp_libieee1284_errorstr(result));
+ return -1;
+ }
+#endif
+
+ port[fd].claimed = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_release( int fd )
+{
+ DBG( 4, "sanei_pp_release: fd = %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if((fd < 0) || (fd >= pplist.portc)) {
+ DBG( 2, "sanei_pp_release: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+
+ ieee1284_release( pplist.portv[fd] );
+#endif
+ port[fd].claimed = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_outb_data( int fd, SANE_Byte val )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_outb_data: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_outb_data: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ outb_data( fd, val );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_outb_ctrl( int fd, SANE_Byte val )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_outb_ctrl: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_outb_ctrl: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ outb_ctrl( fd, val );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_outb_addr( int fd, SANE_Byte val )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_outb_addr: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_outb_addr: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ outb_addr( fd, val );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_outb_epp( int fd, SANE_Byte val )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_outb_epp: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_outb_epp: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ outb_eppdata( fd, val );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Byte
+sanei_pp_inb_data( int fd )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_inb_data: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_inb_data: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ return inb_data( fd );
+}
+
+SANE_Byte
+sanei_pp_inb_stat( int fd )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_inb_stat: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_outb_stat: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ return inb_stat( fd );
+}
+
+SANE_Byte
+sanei_pp_inb_ctrl( int fd )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_inb_ctrl: called for fd %d\n", fd );
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_inb_ctrl: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ return inb_ctrl( fd );
+}
+
+SANE_Byte
+sanei_pp_inb_epp( int fd )
+{
+#ifdef _PARANOIA
+ DBG( 4, "sanei_pp_inb_epp: called for fd %d\n", fd );
+
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_inb_epp: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+#endif
+ return inb_eppdata( fd );
+}
+
+SANE_Status
+sanei_pp_getmodes( int fd, int *mode )
+{
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_getmodes: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+
+ if( mode )
+ *mode = port[fd].caps;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_setmode( int fd, int mode )
+{
+#if defined(HAVE_LIBIEEE1284)
+ int result;
+
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_setmode: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+
+ if((mode != SANEI_PP_MODE_SPP) && (mode != SANEI_PP_MODE_BIDI) &&
+ (mode != SANEI_PP_MODE_EPP) && (mode != SANEI_PP_MODE_ECP)) {
+ DBG( 2, "sanei_pp_setmode: invalid mode %d\n", mode );
+ return SANE_STATUS_INVAL;
+ }
+
+#if defined(HAVE_LIBIEEE1284)
+ switch( mode ) {
+ case SANEI_PP_MODE_SPP: mode = M1284_NIBBLE; break;
+ case SANEI_PP_MODE_BIDI: mode = M1284_BYTE; break;
+ case SANEI_PP_MODE_EPP: mode = M1284_EPP; break;
+ case SANEI_PP_MODE_ECP: mode = M1284_ECP; break;
+
+ default:
+ DBG( 2, "sanei_pp_setmode: invalid mode %d\n", mode );
+ return SANE_STATUS_INVAL;
+ }
+
+ result = ieee1284_negotiate( pplist.portv[fd], mode );
+
+ /* negotiation might fails, but the port-mode should be set... */
+ if((E1284_OK != result) && (E1284_NEGFAILED != result)) {
+ DBG( 2, "sanei_pp_setmode failed: %s\n",
+ pp_libieee1284_errorstr( result ));
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+#else
+ return pp_setmode( fd, mode );
+#endif
+}
+
+void
+sanei_pp_udelay( unsigned long usec )
+{
+ struct timeval now, deadline;
+
+ if( usec == 0 )
+ return;
+
+ gettimeofday( &deadline, NULL );
+ deadline.tv_usec += usec;
+ deadline.tv_sec += deadline.tv_usec / 1000000;
+ deadline.tv_usec %= 1000000;
+
+ if( usec < pp_thresh )
+ return;
+
+ do {
+ gettimeofday( &now, NULL );
+ } while ((now.tv_sec < deadline.tv_sec) ||
+ (now.tv_sec == deadline.tv_sec && now.tv_usec < deadline.tv_usec));
+}
+
+SANE_Status
+sanei_pp_set_datadir( int fd, int rev )
+{
+#if defined(HAVE_LIBIEEE1284)
+ if ((fd < 0) || (fd >= pplist.portc)) {
+#else
+ SANE_Byte tmp;
+
+ if ((fd < 0) || (fd >= NELEMS (port))) {
+#endif
+ DBG( 2, "sanei_pp_setdir: invalid fd %d\n", fd );
+ return SANE_STATUS_INVAL;
+ }
+
+#if defined(HAVE_LIBIEEE1284)
+ ieee1284_data_dir( pplist.portv[fd], rev );
+#else
+ tmp = inb_ctrl( fd );
+ if( SANEI_PP_DATAIN == rev )
+ tmp |= SANEI_PP_CTRL_DIRECTION;
+ else
+ tmp &= ~SANEI_PP_CTRL_DIRECTION;
+ outb_ctrl( fd, tmp );
+#endif
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Bool
+sanei_pp_uses_directio( void )
+{
+#if defined(HAVE_LIBIEEE1284)
+ return SANE_FALSE;
+#else
+ return SANE_TRUE;
+#endif
+}
+
+#else /* !HAVE_IOPERM */
+
+SANE_Status
+sanei_pp_init( void )
+{
+ DBG_INIT();
+ _VAR_NOT_USED( first_time );
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_pp_open( const char *dev, int *fd )
+{
+ if (fd)
+ *fd = -1;
+
+ DBG( 4, "sanei_pp_open: called for device `%s`\n", dev );
+ DBG( 3, "sanei_pp_open: support not compiled\n" );
+ DBG( 6, "sanei_pp_open: basically, this backend does only compile\n" );
+ DBG( 6, "sanei_pp_open: on x86 architectures. Furthermore it\n" );
+ DBG( 6, "sanei_pp_open: needs ioperm() and inb()/outb() calls.\n" );
+ DBG( 6, "sanei_pp_open: alternativly it makes use of libieee1284\n" );
+ DBG( 6, "sanei_pp_open: (which isn't present either)\n");
+ return SANE_STATUS_INVAL;
+}
+
+void
+sanei_pp_close( int fd )
+{
+ DBG( 4, "sanei_pp_close: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_close: fd %d is invalid\n", fd );
+ return;
+}
+
+SANE_Status
+sanei_pp_claim( int fd )
+{
+ DBG( 4, "sanei_pp_claim: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_claim: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pp_release( int fd )
+{
+ DBG( 4, "sanei_pp_release: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_release: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pp_outb_data( int fd, SANE_Byte val )
+{
+ _VAR_NOT_USED( val );
+ DBG( 4, "sanei_pp_outb_data: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_outb_data: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pp_outb_ctrl( int fd, SANE_Byte val )
+{
+ _VAR_NOT_USED( val );
+ DBG( 4, "sanei_pp_outb_ctrl: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_outb_ctrl: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pp_outb_addr( int fd, SANE_Byte val )
+{
+ _VAR_NOT_USED( val );
+ DBG( 4, "sanei_pp_outb_addr: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_outb_addr: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Byte
+sanei_pp_inb_data( int fd )
+{
+ DBG( 4, "sanei_pp_inb_data: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_inb_data: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Byte sanei_pp_inb_stat( int fd )
+{
+ DBG( 4, "sanei_pp_inb_stat: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_inb_stat: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Byte sanei_pp_inb_ctrl( int fd )
+{
+ DBG( 4, "sanei_pp_inb_ctrl: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_inb_ctrl: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Byte sanei_pp_inb_epp ( int fd )
+{
+ DBG( 4, "sanei_pp_inb_epp: called for fd %d\n", fd );
+ DBG( 2, "sanei_pp_inb_epp: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pp_getmodes( int fd, int *mode )
+{
+ _VAR_NOT_USED( mode );
+ DBG( 4, "sanei_pp_getmodes: called for fd %d\n", fd );
+ DBG( 1, "sanei_pp_getmodes: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+sanei_pp_setmode( int fd, int mode )
+{
+ _VAR_NOT_USED( mode );
+ DBG( 4, "sanei_pp_setmode: called for fd %d\n", fd );
+ DBG( 1, "sanei_pp_setmode: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+void
+sanei_pp_udelay( unsigned long usec )
+{
+ _VAR_NOT_USED( usec );
+ _VAR_NOT_USED( pp_thresh );
+ DBG( 2, "sanei_pp_udelay: not supported\n" );
+}
+
+SANE_Status
+sanei_pp_set_datadir( int fd, int rev )
+{
+ _VAR_NOT_USED( rev );
+ DBG( 4, "sanei_pp_setdir: called for fd %d\n", fd );
+ DBG( 1, "sanei_pp_setdir: fd %d is invalid\n", fd );
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Bool
+sanei_pp_uses_directio( void )
+{
+ DBG( 1, "sanei_pp_uses_directio: not supported\n" );
+ return SANE_FALSE;
+}
+
+#endif /* !HAVE_IOPERM */
diff --git a/sanei/sanei_pv8630.c b/sanei/sanei_pv8630.c
new file mode 100644
index 0000000..848558e
--- /dev/null
+++ b/sanei/sanei_pv8630.c
@@ -0,0 +1,223 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000 Adrian Perez Jorge
+ Copyright (C) 2001 Frank Zago
+ Copyright (C) 2001 Marcio Teixeira
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ Interface files for the PowerVision 8630 chip, a USB to
+ parallel converter used in many scanners.
+
+ */
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#define BACKEND_NAME sanei_pv8630
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_pv8630.h"
+
+#define DBG_error 1
+#define DBG_info 5
+
+void
+sanei_pv8630_init (void)
+{
+ DBG_INIT();
+}
+
+/* Write one control byte */
+SANE_Status
+sanei_pv8630_write_byte (int fd, SANEI_PV_Index index, SANE_Byte byte)
+{
+ SANE_Status status;
+
+ DBG(DBG_info, "sanei_pv8630_write_byte - index=%d, byte=%d\n", index, byte);
+ status =
+ sanei_usb_control_msg (fd, 0x40, PV8630_REQ_WRITEBYTE, byte, index, 0,
+ NULL);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_write_byte error\n");
+ return status;
+}
+
+/* Read one control byte */
+SANE_Status
+sanei_pv8630_read_byte (int fd, SANEI_PV_Index index, SANE_Byte * byte)
+{
+ SANE_Status status;
+
+ DBG(DBG_info, "sanei_pv8630_read_byte - index=%d, byte=%p\n", index, byte);
+
+ status =
+ sanei_usb_control_msg (fd, 0xc0, PV8630_REQ_READBYTE, 0, index, 1, byte);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_read_byte error\n");
+ return status;
+}
+
+/* Prepare a bulk read. len is the size of the data going to be
+ * read by pv8630_bulkread(). */
+SANE_Status
+sanei_pv8630_prep_bulkread (int fd, int len)
+{
+ SANE_Status status;
+
+ status =
+ sanei_usb_control_msg (fd, 0x40, PV8630_REQ_EPPBULKREAD, len & 0xffff,
+ len >> 16, 0, NULL);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_prep_bulkread error\n");
+ return status;
+}
+
+/* Prepare a bulk write. len is the size of the data going to be
+ * written by pv8630_bulkwrite(). */
+SANE_Status
+sanei_pv8630_prep_bulkwrite (int fd, int len)
+{
+ SANE_Status status;
+
+ status =
+ sanei_usb_control_msg (fd, 0x40, PV8630_REQ_EPPBULKWRITE, len & 0xffff,
+ len >> 16, 0, NULL);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_prep_bulkwrite error\n");
+ return status;
+}
+
+/* Flush the buffer. */
+SANE_Status
+sanei_pv8630_flush_buffer (int fd)
+{
+ SANE_Status status;
+
+ status =
+ sanei_usb_control_msg (fd, 0x40, PV8630_REQ_FLUSHBUFFER, 0, 0, 0, NULL);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_flush_buffer error\n");
+ return status;
+}
+
+/* Do a bulk write. The length must have previously been sent via
+ * pv8630_prep_bulkwrite(). */
+SANE_Status
+sanei_pv8630_bulkwrite (int fd, const void *data, size_t * len)
+{
+ SANE_Status status;
+
+ status = sanei_usb_write_bulk (fd, (const SANE_Byte *) data, len);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_bulkwrite error\n");
+ return status;
+}
+
+/* Do a bulk read. The length must have previously been sent via
+ * pv8630_prep_bulkread(). */
+SANE_Status
+sanei_pv8630_bulkread (int fd, void *data, size_t * len)
+{
+ SANE_Status status;
+
+ status = sanei_usb_read_bulk (fd, data, len);
+
+ if (status != SANE_STATUS_GOOD)
+ DBG (DBG_error, "sanei_pv8630_bulkread error\n");
+ return status;
+}
+
+/* Expects a specific byte in a register */
+SANE_Status
+sanei_pv8630_xpect_byte (int fd, SANEI_PV_Index index, SANE_Byte value,
+ SANE_Byte mask)
+{
+ SANE_Status status;
+ SANE_Byte s;
+
+ status = sanei_pv8630_read_byte (fd, index, &s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if ((s & mask) != value)
+ {
+ DBG (DBG_error, "sanei_pv8630_xpect_byte: expected %x, got %x\n", value,
+ s);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/* Wait for the status register to present a given status. A timeout value
+ is given in tenths of a second. */
+SANE_Status
+sanei_pv8630_wait_byte (int fd, SANEI_PV_Index index, SANE_Byte value,
+ SANE_Byte mask, int timeout)
+{
+ SANE_Status status;
+ SANE_Byte s;
+ int n;
+
+ for (n = 0; n < timeout; n++)
+ {
+
+ status = sanei_pv8630_read_byte (fd, index, &s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if ((s & mask) == value)
+ return SANE_STATUS_GOOD;
+
+ usleep (100000);
+ }
+
+ DBG (DBG_error, "sanei_pv8630_wait_byte: timeout waiting for %x (got %x)\n",
+ value, s);
+ return SANE_STATUS_IO_ERROR;
+}
diff --git a/sanei/sanei_scsi.c b/sanei/sanei_scsi.c
new file mode 100644
index 0000000..6d171d3
--- /dev/null
+++ b/sanei/sanei_scsi.c
@@ -0,0 +1,6187 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1996, 1997 David Mosberger-Tang
+ Copyright (C) 2003 Frank Zago
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file provides a generic SCSI interface. */
+
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+#include "../include/lassert.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <sys/param.h>
+#include <sys/types.h>
+
+#if defined (HAVE_WINDOWS_H)
+# include <windows.h>
+#endif
+
+#define STUBBED_INTERFACE 0
+#define LINUX_INTERFACE 1
+#define BSD_INTERFACE 2
+#define HPUX_INTERFACE 3
+#define OPENSTEP_INTERFACE 4
+#define DECUNIX_INTERFACE 5
+#define SCO_OS5_INTERFACE 6
+#define IRIX_INTERFACE 7
+#define SOLARIS_INTERFACE 8
+#define SOLARIS_SG_INTERFACE 9
+#define OS2_INTERFACE 10
+#define AIX_GSC_INTERFACE 11
+#define DOMAINOS_INTERFACE 12
+#define FREEBSD_CAM_INTERFACE 13
+#define SYSVR4_INTERFACE 14
+#define SCO_UW71_INTERFACE 15
+#define SOLARIS_USCSI_INTERFACE 16
+#define MACOSX_INTERFACE 17
+#define WIN32_INTERFACE 18
+
+#ifdef HAVE_RESMGR
+# include <resmgr.h>
+#endif
+
+#if defined (HAVE_SCSI_SG_H)
+# define USE LINUX_INTERFACE
+# include <scsi/sg.h>
+#elif defined (HAVE__USR_SRC_LINUX_INCLUDE_SCSI_SG_H)
+# define USE LINUX_INTERFACE
+# include "/usr/src/linux/include/scsi/sg.h"
+#elif defined (HAVE_SYS_SCSICMD)
+# define USE SCSO_OS5_INTERFACE
+# include <sys/scsi.h>
+# include <sys/scsicmd.h>
+#elif defined (HAVE_CAMLIB_H)
+# define USE FREEBSD_CAM_INTERFACE
+# include <stdio.h> /* there is a bug in scsi_all.h */
+# include <cam/cam.h>
+# include <cam/cam_ccb.h>
+# include <cam/scsi/scsi_message.h>
+# include <cam/scsi/scsi_pass.h>
+# include <camlib.h>
+#elif defined (HAVE_SYS_SCSIIO_H)
+# define USE BSD_INTERFACE
+# include <sys/scsiio.h>
+# ifdef HAVE_SCSI_H
+# include <scsi.h>
+# endif
+#elif defined (HAVE_BSD_DEV_SCSIREG_H)
+# define USE OPENSTEP_INTERFACE
+# include <bsd/dev/scsireg.h>
+#elif defined (HAVE_IO_CAM_CAM_H)
+# define USE DECUNIX_INTERFACE
+# include <io/common/iotypes.h>
+# include <io/cam/cam.h>
+# include <io/cam/dec_cam.h>
+# include <io/cam/uagt.h>
+# include <io/cam/scsi_all.h>
+#elif defined (HAVE_SYS_DSREQ_H)
+# define USE IRIX_INTERFACE
+# include <sys/dsreq.h>
+# include <invent.h>
+#elif defined (HAVE_SYS_SCSI_H)
+# include <sys/scsi.h>
+# ifdef HAVE_SYS_SDI_COMM_H
+# ifdef HAVE_SYS_PASSTHRUDEF_H
+# define USE SCO_UW71_INTERFACE
+# include <sys/scsi.h>
+# include <sys/sdi_edt.h>
+# include <sys/sdi.h>
+# include <sys/passthrudef.h>
+# else
+# define USE SYSVR4_INTERFACE /* Unixware 2.x tested */
+# define HAVE_SYSV_DRIVER
+# include <sys/sdi_edt.h>
+# include <sys/sdi_comm.h>
+# endif
+# else
+# ifdef SCTL_READ
+# define USE HPUX_INTERFACE
+# else
+# ifdef HAVE_GSCDDS_H
+# define USE AIX_GSC_INTERFACE
+# include <gscdds.h>
+# else
+ /* This happens for AIX without gsc and possibly other platforms... */
+# endif
+# endif
+# endif
+#elif defined (HAVE_OS2_H)
+# define USE OS2_INTERFACE
+# define INCL_DOSFILEMGR
+# define INCL_DOS
+# define INCL_DOSDEVICES
+# define INCL_DOSDEVIOCTL
+# define INCL_DOSMEMMGR
+# include <os2.h>
+# include "os2_srb.h"
+#elif defined (HAVE_SYS_SCSI_SGDEFS_H)
+# define USE SOLARIS_SG_INTERFACE
+# include <sys/scsi/sgdefs.h>
+#elif defined (HAVE_SYS_SCSI_TARGETS_SCGIO_H)
+# define USE SOLARIS_INTERFACE
+# define SOL2
+# include <sys/scsi/targets/scgio.h>
+#elif defined (HAVE_SYS_SCSI_SCSI_H)
+ /*
+ * the "offical" solaris uscsi(7I) interface; comes last, so that users
+ * installing the SCG/SG driver can still use these generic scsi interfaces
+ */
+# define USE SOLARIS_USCSI_INTERFACE
+# define SOL2
+# include <sys/scsi/scsi.h>
+#elif defined (HAVE_APOLLO_SCSI_H)
+# define USE DOMAINOS_INTERFACE
+# include <signal.h> /* Only used for signal name for KillDomainServer */
+# include <apollo/base.h>
+# include <apollo/ec2.h>
+# include <apollo/error.h>
+# include <apollo/ms.h>
+# include <apollo/mutex.h>
+# include <apollo/scsi.h>
+# include <apollo/time.h>
+# include "sanei_DomainOS.h"
+#elif defined (HAVE_IOKIT_CDB_IOSCSILIB_H) || \
+ defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \
+ defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H)
+# define USE MACOSX_INTERFACE
+# include <CoreFoundation/CoreFoundation.h>
+# include <IOKit/IOKitLib.h>
+# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H
+# include <IOKit/IOCFPlugIn.h>
+# include <IOKit/cdb/IOSCSILib.h>
+# endif
+# ifdef HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H
+/* The def of VERSION causes problems in the following include files */
+# undef VERSION
+# include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h>
+# include <IOKit/scsi/SCSICommandOperationCodes.h>
+# include <IOKit/scsi/SCSITaskLib.h>
+# else
+# ifdef HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H
+/* The def of VERSION causes problems in the following include files */
+# undef VERSION
+# include <IOKit/scsi-commands/SCSICmds_INQUIRY_Definitions.h>
+# include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
+# include <IOKit/scsi-commands/SCSITaskLib.h>
+# endif
+# endif
+#elif defined (HAVE_DDK_NTDDSCSI_H)
+# define USE WIN32_INTERFACE
+# include <ddk/scsi.h>
+# include <ddk/ntddscsi.h>
+#elif defined (HAVE_NTDDSCSI_H)
+# define USE WIN32_INTERFACE
+# include <ntddscsi.h>
+#endif
+
+#ifndef USE
+# define USE STUBBED_INTERFACE
+#endif
+
+#if USE == LINUX_INTERFACE
+# include <dirent.h>
+#endif
+
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_scsi.h"
+
+#define BACKEND_NAME sanei_scsi
+#include "../include/sane/sanei_debug.h"
+
+#if USE == DECUNIX_INTERFACE
+static int cam_fd = -1; /* used for SCSI CAM based interfaces */
+#endif
+
+#if USE == SOLARIS_INTERFACE || USE == SOLARIS_USCSI_INTERFACE
+static int unit_ready (int fd);
+#endif
+
+#ifdef SG_BIG_BUFF
+# define MAX_DATA SG_BIG_BUFF
+#endif
+
+#if USE == SYSVR4_INTERFACE
+# define MAX_DATA 56*1024 /* don't increase or kernel will dump
+ * tested with adsl, adsa and umax backend
+ * it depends on the lowend scsi
+ * drivers . But the most restriction
+ * is in the UNIXWARE KERNEL witch do
+ * not allow more then 64kB DMA transfers */
+static char lastrcmd[16]; /* hold command block of last read command */
+#endif
+
+#if USE == OPENSTEP_INTERFACE
+# define MAX_DATA (120*1024)
+#endif
+
+#if USE == IRIX_INTERFACE
+# define MAX_DATA (256*1024)
+#endif
+
+#if USE == FREEBSD_CAM_INTERFACE
+# define MAX_DATA (DFLTPHYS - PAGE_SIZE)
+#endif
+
+#if USE == SOLARIS_INTERFACE
+# define MAX_DATA (128*1024)
+#endif
+
+#if USE == SOLARIS_SG_INTERFACE
+# define MAX_DATA (128*1024)
+#endif
+
+#if USE == SOLARIS_USCSI_INTERFACE
+# define MAX_DATA (64*1024)
+#endif
+
+#if USE == OS2_INTERFACE
+# define MAX_DATA (64*1024)
+#endif
+
+#if USE == MACOSX_INTERFACE
+# define MAX_DATA (128*1024)
+#endif
+
+
+#ifndef MAX_DATA
+# define MAX_DATA (32*1024)
+#endif
+
+#ifdef SG_SET_TIMEOUT
+# ifdef _SC_CLK_TCK
+# define GNU_HZ sysconf(_SC_CLK_TCK)
+# else
+# ifdef HZ
+# define GNU_HZ HZ
+# else
+# ifdef CLOCKS_PER_SEC
+# define GNU_HZ CLOCKS_PER_SEC
+# endif
+# endif
+# endif
+#endif
+
+/* default timeout value: 120 seconds */
+static int sane_scsicmd_timeout = 120;
+int sanei_scsi_max_request_size = MAX_DATA;
+#if USE == LINUX_INTERFACE
+/* the following #defines follow Douglas Gilbert's sample code
+ to maintain run time compatibility with the old and the
+ new SG driver for Linux
+*/
+#include "linux_sg3_err.h" /* contains several definitions of error codes */
+#ifndef SG_SET_COMMAND_Q
+#define SG_SET_COMMAND_Q 0x2271
+#endif
+#ifndef SG_SET_RESERVED_SIZE
+#define SG_SET_RESERVED_SIZE 0x2275
+#endif
+#ifndef SG_GET_RESERVED_SIZE
+#define SG_GET_RESERVED_SIZE 0x2272
+#endif
+#ifndef SG_GET_SCSI_ID
+#define SG_GET_SCSI_ID 0x2276
+#else
+#define SG_GET_SCSI_ID_FOUND
+#endif
+#ifndef SG_GET_VERSION_NUM
+#define SG_GET_VERSION_NUM 0x2282
+#endif
+#ifndef SG_NEXT_CMD_LEN
+#define SG_NEXT_CMD_LEN 0x2283
+#endif
+
+#ifndef SCSIBUFFERSIZE
+#define SCSIBUFFERSIZE (128 * 1024)
+#endif
+
+/* the struct returned by the SG ioctl call SG_GET_SCSI_ID changed
+ from version 2.1.34 to 2.1.35, and we need the informations from
+ the field s_queue_depth, which was introduced in 2.1.35.
+ To get this file compiling also with older versions of sg.h, the
+ struct is re-defined here.
+*/
+typedef struct xsg_scsi_id
+{
+ int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
+ int channel;
+ int scsi_id; /* scsi id of target device */
+ int lun;
+ int scsi_type; /* TYPE_... defined in scsi/scsi.h */
+ short h_cmd_per_lun; /* host (adapter) maximum commands per lun */
+ short d_queue_depth; /* device (or adapter) maximum queue length */
+ int unused1; /* probably find a good use, set 0 for now */
+ int unused2; /* ditto */
+}
+SG_scsi_id;
+
+typedef struct req
+{
+ struct req *next;
+ int fd;
+ u_int running:1, done:1;
+ SANE_Status status;
+ size_t *dst_len;
+ void *dst;
+/* take the definition of the ioctl parameter SG_IO as a
+ compiler flag if the new SG driver is available
+*/
+ union
+ {
+ struct
+ {
+ struct sg_header hdr;
+ /* Make sure this is the last element, the real size is
+ SG_BIG_BUFF and machine dependant */
+ u_int8_t data[1];
+ }
+ cdb;
+#ifdef SG_IO
+/* at present, Linux's SCSI system limits the sense buffer to 16 bytes
+ which is definitely too small. Hoping that this will change at some time,
+ let's set the sense buffer size to 64.
+*/
+#define SENSE_MAX 64
+#define MAX_CDB 12
+ struct
+ {
+ struct sg_io_hdr hdr;
+ u_char sense_buffer[SENSE_MAX];
+ u_int8_t data[1];
+ }
+ sg3;
+#endif
+ }
+ sgdata;
+}
+req;
+
+typedef struct Fdparms
+{
+ int sg_queue_used, sg_queue_max;
+ size_t buffersize;
+ req *sane_qhead, *sane_qtail, *sane_free_list;
+}
+fdparms;
+
+#endif
+
+#if USE == FREEBSD_CAM_INTERFACE
+# define CAM_MAXDEVS 128
+struct cam_device *cam_devices[CAM_MAXDEVS] = { NULL };
+#endif
+
+static struct
+{
+ u_int in_use:1; /* is this fd_info in use? */
+ u_int fake_fd:1; /* is this a fake file descriptor? */
+ u_int bus, target, lun; /* nexus info; used for some interfaces only */
+ SANEI_SCSI_Sense_Handler sense_handler;
+ void *sense_handler_arg;
+ void *pdata; /* platform-specific data */
+}
+ *fd_info;
+
+static u_char cdb_sizes[8] = {
+ 6, 10, 10, 12, 12, 12, 10, 10
+};
+#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)]
+
+
+#if USE == DOMAINOS_INTERFACE
+
+/*
+ This includes the server code. Most of these routines are private to the
+ actual server. The only public ones are:
+ sanei_DomainOS_init Used to initialize the server
+ DomainErrorCheck A common error handling routine
+ */
+
+#include "sanei_DomainOS.c"
+
+int ServerInitialized = 0;
+pid_t ServerPID;
+struct DomainServerCommon *com;
+long CommandTriggerValue[2];
+ec2_$ptr_t CommandAcceptedPtr[2];
+long ResultTriggerValue[2];
+ec2_$ptr_t ResultReadyPtr[2];
+time_$clock_t Wait16S = { 64, 0 }; /* Delay of about 16 Seconds */
+
+
+/* This function is registered as an exit function. It's purpose is
+ to make sure that the Domain SANE Server is stopped. It tries to
+ send an Exit command, and if that fails, it will send SIGQUIT to
+ the server. It will also unmap the common area before it
+ returns. */
+static void
+KillDomainServer (void)
+{
+ static boolean GotTheLock;
+ static status_$t status;
+ static pinteger index;
+
+ DBG (1, "Asking Domain SANE Server to exit\n");
+ /* First, try to send a command to exit */
+ if (GotTheLock = mutex_$lock (&com->CommandLock, Wait16S))
+ {
+ /* Set the wait time to 16 Seconds (units are 4uS) */
+ com->opcode = Exit;
+ CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1;
+ ec2_$advance (&com->CommandAvailable, &status);
+ DomainErrorCheck (status, "Can't advance CommandAvailable EC");
+ /* For this wait, we want to allow a timeout as well */
+ CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1])
+ + DomainECWaitConstant);
+ index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2,
+ &status);
+ DomainErrorCheck (status,
+ "Error waiting on Exit command acceptance EC");
+ /* Release the lock */
+ mutex_$unlock (&com->CommandLock);
+ if (index == 1)
+ DBG (1, "Domain SANE Server responded to exit request\n");
+ else
+ DBG (1, "Domain SANE Server did not respond to exit request\n");
+ }
+ else
+ DBG (0, "Could not get mutex lock for killing server\n");
+ if ((!GotTheLock) || (index != 1))
+ {
+ /* If we get here, then we never got the mutex lock, or we timed out
+ waiting for an Exit command ack. */
+ /* It's now time to be brutal with the server */
+ DBG (1, "Sending QUIT signal to Domain SANE Server\n");
+ kill (ServerPID, SIGQUIT);
+ }
+ /* unmap the common area */
+ ms_$unmap (com, sizeof (struct DomainServerCommon), &status);
+ DomainErrorCheck (status, "Error unmapping common area");
+}
+#endif /* USE == DOMAINOS_INTERFACE */
+
+
+#if USE == OS2_INTERFACE
+
+/* Driver info: */
+static HFILE driver_handle = 0; /* file handle for device driver */
+static PVOID aspi_buf = 0; /* Big data buffer locked by driver. */
+static int aspi_ref_count = 0; /* # of fds using ASPI */
+static SRB *PSRBlock = 0; /* SCSI Request Block */
+static char tmpAspi[MAXPATHLEN]; /* scsi chain scan */
+#define INQUIRY 0x12
+#define set_inquiry_return_size(icb,val) icb[0x04]=val
+#define IN_periph_devtype_cpu 0x03
+#define IN_periph_devtype_scanner 0x06
+#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)
+#define get_inquiry_periph_devtype(in) (in[0] & 0x1f)
+#define get_inquiry_additional_length(in) in[0x04]
+#define set_inquiry_length(out,n) out[0x04]=n-5
+
+/* Open OS2 ASPI driver.
+
+ Output: 0 if error, which is reported. */
+static int
+open_aspi (void)
+{
+ ULONG rc;
+ ULONG ActionTaken;
+ USHORT lockSegmentReturn;
+ unsigned long cbreturn = 0;
+ unsigned long cbParam = 0;
+ int i, num_adapters; /* no. of scsi adapters installed */
+
+ char *devtypes[] = {
+ "disk", "tape", "printer", "processor", "CD-writer",
+ "CD-drive", "scanner", "optical-drive", "jukebox",
+ "communicator"
+ };
+ FILE *tmp;
+
+ if (driver_handle)
+ {
+ aspi_ref_count++; /* increment internal usage counter */
+ return 1; /* Already open. */
+ }
+ aspi_buf = _tcalloc (sanei_scsi_max_request_size, 1);
+ if (aspi_buf == NULL)
+ {
+ DBG (1, "sanei_scsi_open_aspi: _tcalloc aspi_buf failed");
+ return 0;
+ }
+
+ PSRBlock = _tcalloc (sizeof (SRB), 1);
+ if (PSRBlock == NULL)
+ {
+ DBG (1, "sanei_scsi_open_aspi: _tcalloc PSRBlock failed");
+ return 0;
+ }
+
+ rc = DosOpen ((PSZ) "aspirou$", /* open driver */
+ &driver_handle,
+ &ActionTaken,
+ 0,
+ 0,
+ FILE_OPEN,
+ OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE, NULL);
+ if (rc)
+ {
+ /* opening failed -> return false */
+ DBG (1, "open_aspi: opening failed.\n");
+ return 0;
+ }
+
+ /* Lock aspi_buf. */
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x04, /* pass aspi_buf pointer */
+ (void *) aspi_buf, sizeof (PVOID), /* to driver */
+ &cbParam, (void *) &lockSegmentReturn,
+ sizeof (USHORT), &cbreturn);
+ if (rc || lockSegmentReturn)
+ {
+ /* DosDevIOCtl failed */
+ DBG (1, "sanei_scsi_open_aspi: Can't lock buffer. rc= %lu \n", rc);
+ return 0;
+ }
+
+ /* query number of installed adapters */
+ memset (PSRBlock, 0, sizeof (SRB));
+ PSRBlock->cmd = SRB_Inquiry; /* host adapter inquiry */
+
+ PSRBlock->ha_num = 0; /* host adapter number */
+
+ PSRBlock->flags = 0; /* no flags set */
+
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
+ (void *) PSRBlock, sizeof (SRB), &cbParam,
+ (void *) PSRBlock, sizeof (SRB), &cbreturn);
+ num_adapters = PSRBlock->u.inq.num_ha;
+
+ DBG (1, "OS/2: installed adapters %d\n", num_adapters);
+ DBG (1, "OS/2: ASPI manager is '%s'\n", PSRBlock->u.inq.aspimgr_id);
+ DBG (1, "OS/2: host adapter is '%s'\n", PSRBlock->u.inq.host_id);
+ DBG (1, "OS/2: unique id is '%s'\n", PSRBlock->u.inq.unique_id);
+
+ strcpy (tmpAspi, "asXXXXXX");
+ mktemp (tmpAspi);
+ DBG (2, "open_aspi: open temporary file '%s'\n", tmpAspi);
+ tmp = fopen (tmpAspi, "w");
+ if (!tmp)
+ { /* can't open tmp file */
+
+ DBG (1, "open_aspi: Can't open temporary file.\n");
+ return 0;
+ }
+
+ /* scan all installed adapters */
+ for (i = 0; i < num_adapters; i++)
+ {
+ int id;
+ /* query adapter name */
+ memset (PSRBlock, 0, sizeof (SRB));
+ PSRBlock->cmd = SRB_Inquiry; /* host adapter inquiry */
+
+ PSRBlock->ha_num = i; /* host adapter number */
+
+ PSRBlock->flags = 0; /* no flags set */
+
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
+ (void *) PSRBlock, sizeof (SRB), &cbParam,
+ (void *) PSRBlock, sizeof (SRB), &cbreturn);
+ DBG (1, "OS/2: adapter#%02d '%s'\n", i, PSRBlock->u.inq.host_id);
+
+ /* scan scsi chain (need 15 for wide?) */
+ for (id = 0; id < 7; id++)
+ {
+ unsigned char len;
+ char vendor[9];
+ char product[17];
+ char version[5];
+ char *pp;
+
+ memset (PSRBlock, 0, sizeof (SRB));
+ PSRBlock->cmd = SRB_Device; /* get device type */
+
+ PSRBlock->ha_num = i; /* host adapter number */
+
+ PSRBlock->flags = 0; /* no flags set */
+
+ PSRBlock->u.dev.target = id; /* target id */
+
+ PSRBlock->u.dev.lun = 0; /* target LUN */
+
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
+ (void *) PSRBlock, sizeof (SRB), &cbParam,
+ (void *) PSRBlock, sizeof (SRB), &cbreturn);
+ DBG (1, "OS/2: id#%02d status=%02xh\n",
+ id, PSRBlock->status);
+
+ /* skip if device not connected */
+ if (PSRBlock->status == SRB_BadDevice)
+ continue;
+
+ DBG (1, "OS/2: type is '%s'\n",
+ PSRBlock->u.dev.devtype < sizeof (devtypes) / sizeof (char *)?
+ devtypes[PSRBlock->u.dev.devtype] : "unknown device");
+
+ /* query adapter string id */
+ memset (PSRBlock, 0, sizeof (SRB));
+ PSRBlock->cmd = SRB_Command; /* execute SCSI command */
+
+ PSRBlock->ha_num = i; /* host adapter number */
+ PSRBlock->flags = SRB_Read | SRB_Post; /* data transfer, posting */
+ PSRBlock->u.cmd.target = id; /* Target SCSI ID */
+ PSRBlock->u.cmd.lun = 0; /* Target SCSI LUN */
+ PSRBlock->u.cmd.data_len = 5; /* # of bytes transferred */
+ PSRBlock->u.cmd.sense_len = 32; /* length of sense buffer */
+ PSRBlock->u.cmd.data_ptr = NULL; /* pointer to data buffer */
+ PSRBlock->u.cmd.link_ptr = NULL; /* pointer to next SRB */
+ PSRBlock->u.cmd.cdb_len = 6; /* SCSI command length */
+ PSRBlock->u.cmd.cdb_st[0] = INQUIRY; /* inquiry command */
+ PSRBlock->u.cmd.cdb_st[1] = 0; /* ?? length */
+ PSRBlock->u.cmd.cdb_st[2] = 0; /* transfer length MSB */
+ PSRBlock->u.cmd.cdb_st[3] = 0; /* transfer length */
+ PSRBlock->u.cmd.cdb_st[4] = 5; /* transfer length LSB */
+ PSRBlock->u.cmd.cdb_st[5] = 0;
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
+ (void *) PSRBlock, sizeof (SRB), &cbParam,
+ (void *) PSRBlock, sizeof (SRB), &cbreturn);
+ len = ((char *) aspi_buf)[4]; /* additional length */
+
+ /* query id string */
+ memset (PSRBlock, 0, sizeof (SRB));
+ PSRBlock->cmd = SRB_Command; /* execute SCSI command */
+ PSRBlock->ha_num = i; /* host adapter number */
+ PSRBlock->flags = SRB_Read | SRB_Post; /* data transfer, posting */
+ PSRBlock->u.cmd.target = id; /* Target SCSI ID */
+ PSRBlock->u.cmd.lun = 0; /* Target SCSI LUN */
+ PSRBlock->u.cmd.data_len = 5 + len; /* # of bytes transferred */
+ PSRBlock->u.cmd.sense_len = 32; /* length of sense buffer */
+ PSRBlock->u.cmd.data_ptr = NULL; /* pointer to data buffer */
+ PSRBlock->u.cmd.link_ptr = NULL; /* pointer to next SRB */
+ PSRBlock->u.cmd.cdb_len = 6; /* SCSI command length */
+ PSRBlock->u.cmd.cdb_st[0] = 0x12; /* inquiry command */
+ PSRBlock->u.cmd.cdb_st[1] = 0; /* ?? length */
+ PSRBlock->u.cmd.cdb_st[2] = 0; /* transfer length MSB */
+ PSRBlock->u.cmd.cdb_st[3] = 0; /* transfer length */
+ PSRBlock->u.cmd.cdb_st[4] = 5 + len; /* transfer length LSB */
+ PSRBlock->u.cmd.cdb_st[5] = 0;
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
+ (void *) PSRBlock, sizeof (SRB), &cbParam,
+ (void *) PSRBlock, sizeof (SRB), &cbreturn);
+ DBG (1, "OS/2 '%s'\n", (char *) aspi_buf + 8);
+ /* write data */
+ get_inquiry_vendor ((char *) aspi_buf, vendor);
+ get_inquiry_product ((char *) aspi_buf, product);
+ get_inquiry_version ((char *) aspi_buf, version);
+
+ pp = &vendor[7];
+ vendor[8] = '\0';
+ while (pp >= vendor && (*pp == ' ' || *pp >= 127))
+ *pp-- = '\0';
+
+ pp = &product[15];
+ product[16] = '\0';
+ while (pp >= product && (*pp == ' ' || *pp >= 127))
+ *pp-- = '\0';
+
+ pp = product;
+ do
+ {
+ if (isspace ((int) *pp))
+ *pp = '_';
+ }
+ while (*++pp);
+
+ pp = &version[3];
+ version[4] = '\0';
+ while (pp >= version && (*pp == ' ' || *(pp - 1) >= 127))
+ *pp-- = '\0';
+ fprintf (tmp, "Vendor: %s ", vendor);
+ fprintf (tmp, "Model: %s ", product);
+ fprintf (tmp, "Rev: %s ", version);
+ fprintf (tmp, "scsi %d Channel: 0 Id: %d Lun: 0\n", i, id);
+ }
+ }
+ DBG (2, "open_aspi: close temporary file '%s'\n", tmpAspi);
+ fclose (tmp);
+
+ aspi_ref_count++; /* increment internal usage counter */
+
+ return 1;
+}
+
+/* Close driver and free everything. */
+
+static void
+close_aspi (void)
+{
+ aspi_ref_count--; /* decrement internal usage counter */
+
+ if (aspi_ref_count)
+ return; /* wait for usage==0 */
+
+ if (driver_handle) /* Close driver. */
+ DosClose (driver_handle);
+ driver_handle = 0;
+ if (aspi_buf) /* Free buffer. */
+ _tfree (aspi_buf);
+ aspi_buf = 0;
+
+ if (PSRBlock)
+ _tfree (PSRBlock);
+ PSRBlock = 0;
+
+ errno = 0;
+ if (unlink (tmpAspi)) /* remove scsi descriptions */
+ DBG (2, "OS/2: error#%d while removing temporary '%s'\n", errno, tmpAspi);
+ strcpy (tmpAspi, "");
+
+ DBG (1, "OS/2: ASPI closed\n");
+}
+
+#endif /* USE_OS2_INTERFACE */
+
+static int num_alloced = 0;
+
+#if USE == LINUX_INTERFACE
+
+static int sg_version = 0;
+
+static SANE_Status
+get_max_buffer_size (const char *file)
+{
+ int fd = -1;
+ int buffersize = SCSIBUFFERSIZE, i;
+ size_t len;
+ char *cc, *cc1, buf[32];
+
+#ifdef HAVE_RESMGR
+ fd = rsm_open_device(file, O_RDWR);
+#endif
+
+ if (fd == -1)
+ fd = open (file, O_RDWR);
+
+ if (fd > 0)
+ {
+ cc = getenv ("SANE_SG_BUFFERSIZE");
+ if (cc)
+ {
+ i = strtol (cc, &cc1, 10);
+ if (cc != cc1 && i >= 32768)
+ buffersize = i;
+ }
+
+ ioctl (fd, SG_SET_RESERVED_SIZE, &buffersize);
+ if (0 == ioctl (fd, SG_GET_RESERVED_SIZE, &buffersize))
+ {
+ if (buffersize < sanei_scsi_max_request_size)
+ sanei_scsi_max_request_size = buffersize;
+ close (fd);
+ DBG (4, "get_max_buffer_size for %s: %i\n", file,
+ sanei_scsi_max_request_size);
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ close (fd);
+ /* ioctl not available: we have the old SG driver */
+ fd = open ("/proc/sys/kernel/sg-big-buff", O_RDONLY);
+ if (fd > 0 && (len = read (fd, buf, sizeof (buf) - 1)) > 0)
+ {
+ buf[len] = '\0';
+ sanei_scsi_max_request_size = atoi (buf);
+ close (fd);
+ }
+ else
+ sanei_scsi_max_request_size = buffersize < SG_BIG_BUFF ?
+ buffersize : SG_BIG_BUFF;
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sanei_scsi_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler,
+ void *handler_arg, int *buffersize)
+#else
+
+SANE_Status
+sanei_scsi_open (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+#endif
+{
+ u_int bus = 0, target = 0, lun = 0, fake_fd = 0;
+ char *real_dev = 0;
+ void *pdata = 0;
+ char *cc, *cc1;
+ int fd, i;
+#if USE == LINUX_INTERFACE
+ static int first_time = 1;
+#elif USE == MACOSX_INTERFACE
+ UInt8 *guid;
+ int len;
+ u_int d;
+#endif
+
+ cc = getenv ("SANE_SCSICMD_TIMEOUT");
+ if (cc)
+ {
+ i = strtol (cc, &cc1, 10);
+ /* 20 minutes are hopefully enough as a timeout value ;) */
+ if (cc != cc1 && i > 0 && i <= 1200)
+ {
+ sane_scsicmd_timeout = i;
+ }
+ else
+ {
+ DBG (1,
+ "sanei_scsi_open: timeout value must be between 1 and 1200 seconds\n");
+ }
+ }
+
+ DBG_INIT ();
+
+#if USE == LINUX_INTERFACE
+ if (first_time)
+ {
+ first_time = 0;
+
+ /* Try to determine a reliable value for sanei_scsi_max_request_size:
+
+ With newer versions of the SG driver, check the available buffer
+ size by opening all SG device files belonging to a scanner,
+ issue the ioctl calls for setting and reading the reserved
+ buffer size, and take the smallest value.
+
+ For older version of the SG driver, which don't support variable
+ buffer size, try to read /proc/sys/kernel/sg-big-biff ; if
+ this fails (SG driver too old, or loaded as a module), use
+ SG_BIG_BUFF
+ */
+
+ sanei_scsi_max_request_size = SCSIBUFFERSIZE;
+ cc = getenv ("SANE_SG_BUFFERSIZE");
+ if (cc)
+ {
+ i = strtol (cc, &cc1, 10);
+ if (cc != cc1 && i >= 32768)
+ sanei_scsi_max_request_size = i;
+ }
+ sanei_scsi_find_devices (0, 0, "Scanner", -1, -1, -1, -1,
+ get_max_buffer_size);
+ sanei_scsi_find_devices (0, 0, "Processor", -1, -1, -1, -1,
+ get_max_buffer_size);
+ DBG (4, "sanei_scsi_open: sanei_scsi_max_request_size=%d bytes\n",
+ sanei_scsi_max_request_size);
+ }
+#endif
+
+#if USE == OS2_INTERFACE
+ if (sscanf (dev, "b%ut%ul%u", &bus, &target, &lun) != 3)
+ {
+ DBG (1, "sanei_scsi_open: device name %s is not valid\n", dev);
+ return SANE_STATUS_INVAL;
+ }
+ if (!open_aspi ())
+ {
+ /* Open driver if necessary. */
+ close_aspi ();
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Find fake fd. */
+ for (fd = 0; fd < num_alloced; ++fd)
+ if (!fd_info[fd].in_use)
+ break;
+ fake_fd = 1;
+#elif USE == DECUNIX_INTERFACE
+ {
+ UAGT_CAM_SCAN cam_scan;
+
+ if (sscanf (dev, "b%dt%dl%d", &bus, &target, &lun) != 3)
+ {
+ DBG (1, "sanei_scsi_open: device name `%s´ is not valid: %s\n",
+ dev, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (cam_fd < 0)
+ {
+ cam_fd = open ("/dev/cam", O_RDWR);
+ if (cam_fd < 0)
+ {
+ DBG (1, "sanei_scsi_open: open(/dev/cam) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ }
+ cam_scan.ucs_bus = bus;
+ cam_scan.ucs_target = target;
+ cam_scan.ucs_lun = lun;
+ if (ioctl (cam_fd, UAGT_CAM_SINGLE_SCAN, &cam_scan) < 0)
+ {
+ DBG (1, "sanei_scsi_open: ioctl(UAGT_CAM_SINGLE_SCAN) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+
+ for (fd = 0; fd < num_alloced; ++fd)
+ if (!fd_info[fd].in_use)
+ break;
+ fake_fd = 1;
+ }
+#elif USE == DOMAINOS_INTERFACE
+ {
+ static int index;
+ static status_$t status;
+ static unsigned long length_mapped;
+
+ DBG (1, "sanei_scsi_open: (dev='%s', int * fdp=%p, "
+ "SANEI_SCSI_Sense_Handler handler=%p)\n", dev, fdp, handler);
+
+ /* See if the server process has started yet */
+ if (!ServerInitialized)
+ {
+ static char *CommonAreaPath;
+
+ /* Initialize the server */
+ DBG (2, "Initializing Domain Server\n");
+
+ /* Map the area */
+ CommonAreaPath = tmpnam (NULL);
+ DBG (2, "Domain Server Common area name is '%s'\n", CommonAreaPath);
+ com = ms_$crmapl (CommonAreaPath, strlen (CommonAreaPath), 0,
+ sizeof (struct DomainServerCommon), ms_$cowriters,
+ &status);
+ DomainErrorCheck (status, "Can't open common area");
+ DBG (2, "Domain Server common area mapped\n");
+
+ /* Initialize the eventcounts */
+ ec2_$init (&com->CommandAvailable);
+ ec2_$init (&com->CommandAccepted);
+ ec2_$init (&com->ResultReady);
+ ec2_$init (&com->ResultAccepted);
+ DBG (2, "Domain Server EC's initialized\n");
+ /* Initialize the mutex locks */
+ mutex_$init (&com->CommandLock);
+ mutex_$init (&com->ResultLock);
+ DBG (2, "Domain Server MutexLock's initialized\n");
+
+ /* Initialize pointers to ECs */
+ CommandAcceptedPtr[0] = &com->CommandAccepted;
+ ResultReadyPtr[0] = &com->ResultReady;
+ time_$get_ec (time_$clockh_key, &CommandAcceptedPtr[1], &status);
+ DomainErrorCheck (status, "Can't get time EC");
+ ResultReadyPtr[1] = CommandAcceptedPtr[1];
+
+ /* Read the ResultReady EC value, to avoid race with the server */
+ ResultTriggerValue[0] = ec2_$read (com->ResultReady) + 1;
+
+ /* Now invoke the server */
+ ServerPID = fork ();
+ if (!ServerPID)
+ {
+ /* I am the child, call the initialization routine */
+ sanei_DomainOS_init (CommonAreaPath);
+ /* We get here when the server is done, so we just exit. */
+ exit (EXIT_SUCCESS);
+ }
+
+ /* The communication area is open, wait for the initial response */
+ ResultTriggerValue[1] = (ec2_$read (*ResultReadyPtr[1])
+ + DomainECWaitConstant);
+ index =
+ ec2_$wait_svc (ResultReadyPtr, ResultTriggerValue, 2, &status);
+ DomainErrorCheck (status, "Error waiting on initial open EC");
+ if (index != 1)
+ {
+ DBG (0, "Domain SANE Server never responded on startup\n");
+ /* Send a quit signal to the server */
+ kill (ServerPID, SIGQUIT);
+ return SANE_STATUS_INVAL;
+ }
+ /* Register a function to kill the server when we are done */
+ assert (!atexit (KillDomainServer));
+ ServerInitialized = 1;
+ }
+
+ /* Find fake fd. */
+ for (fd = 0; fd < num_alloced; ++fd)
+ if (!fd_info[fd].in_use)
+ break;
+ fake_fd = 1;
+
+ /* Send the command open to the server */
+ if (!mutex_$lock (&com->CommandLock, Wait16S))
+ {
+ DBG (0, "Could not obtain mutex lock for Open\n");
+ return SANE_STATUS_INVAL;
+ }
+ com->opcode = Open;
+ strcpy (com->open_path, dev);
+ CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1;
+ ec2_$advance (&com->CommandAvailable, &status);
+ DomainErrorCheck (status, "Can't advance CommandAvailable EC");
+ CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1])
+ + DomainECWaitConstant);
+ index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2,
+ &status);
+ DomainErrorCheck (status, "Error waiting on Open command acceptance EC");
+ if (index != 1)
+ {
+ DBG (0, "Domain SANE Server never accepted Open Command\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Read the result */
+ status = com->CommandStatus;
+ DomainErrorCheck (status, "Opening device in server");
+
+ /* Now map the data area, and make it temporary */
+ DBG (2, "Mapping server's data block, name is '%s'\n", com->open_path);
+ pdata = ms_$mapl (com->open_path, strlen (com->open_path), 0,
+ DomainMaxDataSize + DomainSenseSize, ms_$cowriters,
+ ms_$wr, true, &length_mapped, &status);
+ DomainErrorCheck (status, "Mapping Server Data block");
+ assert (length_mapped >= DomainMaxDataSize + DomainSenseSize);
+ ms_$mk_temporary (pdata, &status);
+ DomainErrorCheck (status, "Can't make data block temporary");
+
+ /* Release the lock */
+ mutex_$unlock (&com->CommandLock);
+
+ if (status.all != status_$ok)
+ {
+ /* we have a failure, return an error code, and generate debug
+ output */
+ DBG (1, "sanei_scsi_open: acquire failed, Domain/OS status is %08x\n",
+ status.all);
+ error_$print (status);
+ return SANE_STATUS_INVAL;
+ }
+ else
+ {
+ /* device acquired, what else to do? */
+ fd = com->fd;
+ }
+ }
+#elif USE == FREEBSD_CAM_INTERFACE
+ if (1)
+ { /* 'if(1) {' makes my emacs c-mode indent better than
+ just '{' unfortunately, this only works if all of
+ the '{' are that way. */
+
+ struct cam_device *curdev;
+
+ fake_fd = 1;
+ fd = -1;
+
+ if ((curdev = cam_open_pass (dev, O_RDWR, NULL)) != NULL)
+ {
+ for (fd = 0; fd < CAM_MAXDEVS && cam_devices[fd] != NULL; fd++)
+ ;
+
+ if (fd == CAM_MAXDEVS)
+ {
+ DBG (1, "sanei_scsi_open: too many CAM devices (%d)\n", fd);
+ cam_close_device (curdev);
+ return SANE_STATUS_INVAL;
+ }
+ cam_devices[fd] = curdev;
+ }
+ else
+ {
+ DBG (1, "sanei_scsi_open: can't open device `%s´: %s\n", dev,
+ strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif USE == SCO_UW71_INTERFACE
+ {
+ pt_scsi_address_t dev_addr;
+ pt_handle_t pt_handle;
+ int bus, cnt, id, lun;
+
+ if (4 !=
+ sscanf (dev, "/dev/passthru0:%d,%d,%d,%d", &bus, &cnt, &id, &lun))
+ {
+ DBG (1, "sanei_scsi_open: device name `%s´ is not valid: %s\n",
+ dev, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ dev_addr.psa_bus = bus;
+ dev_addr.psa_controller = cnt;
+ dev_addr.psa_target = id;
+ dev_addr.psa_lun = lun;
+
+ if (0 != pt_open (PASSTHRU_SCSI_ADDRESS, &dev_addr, PT_EXCLUSIVE,
+ &pt_handle))
+ {
+ DBG (1, "sanei_scsi_open: pt_open failed: %s!\n", strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ else
+ fd = (int) pt_handle;
+ }
+#elif USE == MACOSX_INTERFACE
+ {
+# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \
+ defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H)
+ len = strlen (dev);
+ if (len > 2 && len % 2 == 0 && dev [0] == '<' && dev [len - 1] == '>')
+ {
+ len = (len - 2) / 2;
+ guid = (UInt8 *) malloc (len);
+ for (i = 0; i < len; i++)
+ {
+ if (sscanf (&dev [2 * i + 1], "%02x", &d) != 1)
+ break;
+ guid [i] = d;
+ }
+ if (i == len)
+ pdata = (void *) CFDataCreate (kCFAllocatorDefault, guid, len);
+ free (guid);
+ }
+# endif
+# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H
+ if ((pdata == NULL) &&
+ (sscanf (dev, "u%ut%ul%u", &bus, &target, &lun) != 3))
+# else
+ if (pdata == NULL)
+# endif
+ {
+ DBG (1, "sanei_scsi_open: device name %s is not valid\n", dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Find fake fd. */
+ for (fd = 0; fd < num_alloced; ++fd)
+ if (!fd_info[fd].in_use)
+ break;
+ fake_fd = 1;
+ }
+#elif USE == WIN32_INTERFACE
+ {
+ char scsi_hca_name[20];
+ u_int hca = 0;
+
+ if (sscanf (dev, "h%ub%ut%ul%u", &hca, &bus, &target, &lun) != 4)
+ {
+ DBG (1, "sanei_scsi_open: device name %s is not valid\n", dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ snprintf(scsi_hca_name, 19, "\\\\.\\Scsi%d:", hca);
+ scsi_hca_name[19] = 0;
+
+ fd = CreateFile(scsi_hca_name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_RANDOM_ACCESS, NULL );
+
+ if (fd == INVALID_HANDLE_VALUE) fd = -1;
+ }
+#else
+#if defined(SGIOCSTL) || (USE == SOLARIS_INTERFACE)
+ {
+ size_t len;
+
+ /* OpenStep and the Solaris SCG driver are a bit broken in that
+ the device name refers to a scsi _bus_, not an individual scsi
+ device. Hence, SANE has to fudge with the device name so we
+ know which target to connect to. For this purpose, we use the
+ last character in the device name as the target index. 'a' is
+ target 0, 'b', target 1, and so on... */
+
+ len = strlen (dev);
+ if (len <= 1)
+ {
+ DBG (1, "sanei_scsi_open: devicename `%s' too short\n", dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ real_dev = strdup (dev);
+ real_dev[len - 1] = '\0';
+
+ target = dev[len - 1] - 'a';
+ if (target > 7)
+ {
+ DBG (1, "sanei_scsi_open: `%c' is not a valid target id\n",
+ dev[len - 1]);
+ return SANE_STATUS_INVAL;
+ }
+ dev = real_dev;
+ }
+#endif /* defined(SGIOCSTL) || (USE == SOLARIS_INTERFACE) */
+
+ fd = -1;
+#ifdef HAVE_RESMGR
+ fd = rsm_open_device(dev, O_RDWR | O_EXCL | O_NONBLOCK);
+#endif
+
+ if (fd == -1)
+ fd = open (dev, O_RDWR | O_EXCL
+#if USE == LINUX_INTERFACE
+ | O_NONBLOCK
+#endif
+ );
+ if (fd < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ if (errno == EACCES)
+ status = SANE_STATUS_ACCESS_DENIED;
+ else if (errno == EBUSY)
+ status = SANE_STATUS_DEVICE_BUSY;
+
+ DBG (1, "sanei_scsi_open: open of `%s' failed: %s\n",
+ dev, strerror (errno));
+ return status;
+ }
+
+ if (real_dev)
+ free (real_dev);
+
+#ifdef SG_SET_TIMEOUT
+ /* Set large timeout since some scanners are slow but do not
+ disconnect... ;-( */
+ {
+ int timeout;
+ timeout = sane_scsicmd_timeout * GNU_HZ;
+ ioctl (fd, SG_SET_TIMEOUT, &timeout);
+ }
+#endif
+
+#ifdef SGIOCSTL
+ {
+ struct scsi_adr sa;
+
+ sa.sa_target = target;
+ sa.sa_lun = 0;
+ if (ioctl (fd, SGIOCSTL, &sa) == -1)
+ {
+ DBG (1, "sanei_scsi_open: failed to attach to target: %u (%s)\n",
+ sa.sa_target, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ }
+#endif /* SGIOCSTL */
+#if USE == LINUX_INTERFACE
+ {
+ SG_scsi_id sid;
+ int ioctl_val;
+ int real_buffersize;
+ fdparms *fdpa = 0;
+ SG_scsi_id devinfo;
+
+ pdata = fdpa = malloc (sizeof (fdparms));
+ if (!pdata)
+ {
+ close (fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (fdpa, 0, sizeof (fdparms));
+ /* default: allow only one command to be sent to the SG driver
+ */
+ fdpa->sg_queue_max = 1;
+
+ /* Try to read the SG version. If the ioctl call is successful,
+ we have the new SG driver, and we can increase the buffer size
+ using another ioctl call.
+ If we have SG version 2.1.35 or above, we can additionally enable
+ command queueing.
+ */
+ if (0 == ioctl (fd, SG_GET_VERSION_NUM, &sg_version))
+ {
+ DBG (1, "sanei_scsi_open: SG driver version: %i\n", sg_version);
+
+ ioctl_val = ioctl (fd, SG_GET_SCSI_ID, &devinfo);
+ if (ioctl_val == EINVAL || ioctl_val == ENOTTY)
+ {
+ DBG (1, "sanei_scsi_open: The file %s is not an SG device file\n",
+ dev);
+ close (fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (devinfo.scsi_type != 6 && devinfo.scsi_type != 3)
+ {
+ DBG (1,
+ "sanei_scsi_open: The device found for %s does not look like a scanner\n",
+ dev);
+ close (fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* try to reserve a SG buffer of the size specified by *buffersize
+ */
+ ioctl (fd, SG_SET_RESERVED_SIZE, buffersize);
+
+ /* the set call may not be able to allocate as much memory
+ as requested, thus we read the actual buffer size.
+ */
+ if (0 == ioctl (fd, SG_GET_RESERVED_SIZE, &real_buffersize))
+ {
+ /* if we got more memory than requested, we stick with
+ with the requested value, in order to allow
+ sanei_scsi_open to check the buffer size exactly.
+ */
+ if (real_buffersize < *buffersize)
+ *buffersize = real_buffersize;
+ fdpa->buffersize = *buffersize;
+ }
+ else
+ {
+ DBG (1, "sanei_scsi_open: cannot read SG buffer size - %s\n",
+ strerror (errno));
+ close (fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG (1, "sanei_scsi_open_extended: using %i bytes as SCSI buffer\n",
+ *buffersize);
+
+ if (sg_version >= 20135)
+ {
+ DBG (1, "trying to enable low level command queueing\n");
+
+ if (0 == ioctl (fd, SG_GET_SCSI_ID, &sid))
+ {
+ DBG (1, "sanei_scsi_open: Host adapter queue depth: %i\n",
+ sid.d_queue_depth);
+
+ ioctl_val = 1;
+ if (0 == ioctl (fd, SG_SET_COMMAND_Q, &ioctl_val))
+ {
+ fdpa->sg_queue_max = sid.d_queue_depth;
+ if (fdpa->sg_queue_max <= 0)
+ fdpa->sg_queue_max = 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* we have a really old SG driver version, or we're not opening
+ an SG device file
+ */
+ if (ioctl (fd, SG_GET_TIMEOUT, &ioctl_val) < 0)
+ {
+ DBG (1, "sanei_scsi_open: The file %s is not an SG device file\n",
+ dev);
+ close (fd);
+ return SANE_STATUS_INVAL;
+ }
+ if (sanei_scsi_max_request_size < *buffersize)
+ *buffersize = sanei_scsi_max_request_size;
+ fdpa->buffersize = *buffersize;
+ }
+ if (sg_version == 0)
+ {
+ DBG (1, "sanei_scsi_open: using old SG driver logic\n");
+ }
+ else
+ {
+ DBG (1,
+ "sanei_scsi_open: SG driver can change buffer size at run time\n");
+ if (fdpa->sg_queue_max > 1)
+ DBG (1, "sanei_scsi_open: low level command queueing enabled\n");
+#ifdef SG_IO
+ if (sg_version >= 30000)
+ {
+ DBG (1, "sanei_scsi_open: using new SG header structure\n");
+ }
+#endif
+ }
+ }
+#endif /* LINUX_INTERFACE */
+#endif /* !DECUNIX_INTERFACE */
+
+/* Note: this really relies on fd to start small. Windows starts a little higher than 3. */
+
+ if (fd >= num_alloced)
+ {
+ size_t new_size, old_size;
+
+ old_size = num_alloced * sizeof (fd_info[0]);
+ num_alloced = fd + 8;
+ new_size = num_alloced * sizeof (fd_info[0]);
+ if (fd_info)
+ fd_info = realloc (fd_info, new_size);
+ else
+ fd_info = malloc (new_size);
+ memset ((char *) fd_info + old_size, 0, new_size - old_size);
+ if (!fd_info)
+ {
+ if (!fake_fd)
+ close (fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ fd_info[fd].in_use = 1;
+ fd_info[fd].sense_handler = handler;
+ fd_info[fd].sense_handler_arg = handler_arg;
+ fd_info[fd].fake_fd = fake_fd;
+ fd_info[fd].bus = bus;
+ fd_info[fd].target = target;
+ fd_info[fd].lun = lun;
+ fd_info[fd].pdata = pdata;
+
+#if USE == SOLARIS_INTERFACE || USE == SOLARIS_USCSI_INTERFACE
+ /* verify that the device really exists: */
+ if (!unit_ready (fd))
+ {
+ sanei_scsi_close (fd);
+ return SANE_STATUS_INVAL;
+ }
+#endif
+#if USE == SYSVR4_INTERFACE
+ memset (lastrcmd, 0, 16); /* reinitialize last read command block */
+#endif
+
+ if (fdp)
+ *fdp = fd;
+
+ return SANE_STATUS_GOOD;
+}
+
+#if USE == LINUX_INTERFACE
+/* The "wrapper" for the old open call */
+SANE_Status
+sanei_scsi_open (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ int i = 0;
+ int wanted_buffersize = SCSIBUFFERSIZE, real_buffersize;
+ SANE_Status res;
+ char *cc, *cc1;
+ static int first_time = 1;
+
+ if (first_time)
+ {
+ cc = getenv ("SANE_SG_BUFFERSIZE");
+ if (cc)
+ {
+ i = strtol (cc, &cc1, 10);
+ if (cc != cc1 && i >= 32768)
+ wanted_buffersize = i;
+ }
+ }
+ else
+ wanted_buffersize = sanei_scsi_max_request_size;
+
+ real_buffersize = wanted_buffersize;
+ res = sanei_scsi_open_extended (dev, fdp, handler, handler_arg,
+ &real_buffersize);
+
+ /* make sure that we got as much memory as we wanted, otherwise
+ the backend might be confused
+ */
+ if (!first_time && real_buffersize != wanted_buffersize)
+ {
+ DBG (1, "sanei_scsi_open: could not allocate SG buffer memory "
+ "wanted: %i got: %i\n", wanted_buffersize, real_buffersize);
+ sanei_scsi_close (*fdp);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ first_time = 0;
+ return res;
+}
+#else
+/* dummy for the proposed new open call */
+SANE_Status
+sanei_scsi_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler,
+ void *handler_arg, int *buffersize)
+{
+ SANE_Status res;
+ res = sanei_scsi_open (dev, fdp, handler, handler_arg);
+ if (sanei_scsi_max_request_size < *buffersize)
+ *buffersize = sanei_scsi_max_request_size;
+ return res;
+}
+#endif
+
+void
+sanei_scsi_close (int fd)
+{
+#if USE == LINUX_INTERFACE
+ if (fd_info[fd].pdata)
+ {
+ req *req, *next_req;
+
+ /* make sure that there are no pending SCSI calls */
+ sanei_scsi_req_flush_all_extended (fd);
+
+ req = ((fdparms *) fd_info[fd].pdata)->sane_free_list;
+ while (req)
+ {
+ next_req = req->next;
+ free (req);
+ req = next_req;
+ }
+ free (fd_info[fd].pdata);
+ }
+#endif
+
+ fd_info[fd].in_use = 0;
+ fd_info[fd].sense_handler = 0;
+ fd_info[fd].sense_handler_arg = 0;
+
+#ifdef WIN32
+ CloseHandle(fd);
+#else
+ if (!fd_info[fd].fake_fd)
+ close (fd);
+#endif
+
+#if USE == FREEBSD_CAM_INTERFACE
+ cam_close_device (cam_devices[fd]);
+ cam_devices[fd] = NULL;
+#elif USE == DOMAINOS_INTERFACE
+ {
+ static int index;
+ static status_$t status;
+
+ DBG (1, "sanei_scsi_close: fd=%d\n", fd);
+
+ /* Send the command to the server */
+ if (!mutex_$lock (&com->CommandLock, Wait16S))
+ {
+ DBG (0, "Could not obtain mutex lock for Close command\n");
+ }
+ else
+ {
+ com->opcode = Close;
+ com->fd = fd;
+ CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1;
+ ec2_$advance (&com->CommandAvailable, &status);
+ DomainErrorCheck (status, "Can't advance CommandAvailable EC");
+ CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1])
+ + DomainECWaitConstant);
+ index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2,
+ &status);
+ DomainErrorCheck (status,
+ "Error waiting on Close command acceptance EC");
+ if (index != 1)
+ {
+ DBG (0, "Domain SANE Server never accepted Close Command\n");
+ }
+
+ /* Read the result */
+ status = com->CommandStatus;
+ /* Release the lock */
+ mutex_$unlock (&com->CommandLock);
+ }
+
+ /* Unmap the data area */
+ ms_$unmap (fd_info[com->fd].pdata, DomainMaxDataSize + DomainSenseSize,
+ &status);
+ DomainErrorCheck (status, "Error unmapping device data area");
+ }
+#endif /* USE == DOMAINOS_INTERFACE */
+
+#if USE == OS2_INTERFACE
+ close_aspi ();
+#endif /* USE == OS2_INTERFACE */
+
+#if USE == MACOSX_INTERFACE
+ if (fd_info[fd].pdata)
+ CFRelease (fd_info[fd].pdata);
+#endif /* USE == MACOSX_INTERFACE */
+}
+
+
+#if USE == DOMAINOS_INTERFACE
+# define WE_HAVE_ASYNC_SCSI
+
+void
+sanei_scsi_req_flush_all (void)
+{
+ status_$t status;
+
+ DBG (1, "sanei_scsi_req_flush_all: ()\n");
+ /* I have never seen this called, and I'm not sure what to do with it,
+ so I guarantee that it will generate a fault, and I can add support
+ for it. */
+ assert (1 == 0);
+}
+
+
+SANE_Status
+sanei_scsi_req_enter2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+{
+ SANEI_SCSI_Sense_Handler handler;
+ static int index;
+ static SANE_Status sane_status;
+ static status_$t status;
+ static scsi_$status_t SCSIStatus;
+ static void *buf_ptr;
+
+ if (dst_size)
+ DBG (1, "sanei_scsi_req_enter2: (fd=%x, cmd=%p, cmd_size=%x, "
+ "src=%p, src_size=%x, dst=%p, dst_size=%x, *idp=%p)\n",
+ fd, cmd, cmd_size, src, src_size, dst, *dst_size, idp);
+ else
+ DBG (1, "sanei_scsi_req_enter2: (fd=%x, cmd=%p, cmd_size=%x, "
+ "src=%p, src_size=%x, dst=%p, dst_size=NULL, *idp=%p)\n",
+ fd, src, src_size, dst, idp);
+
+ /* Lock the command structure */
+ if (!mutex_$lock (&com->CommandLock, mutex_$wait_forever))
+ {
+ DBG (0, "Could not obtain mutex lock for Enter Command\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill in the command structure */
+ com->opcode = Enter;
+ com->fd = fd;
+ com->cdb_size = cmd_size;
+ if (dst_size)
+ com->dst_size = *dst_size;
+ memcpy (&com->cdb, cmd, com->cdb_size);
+
+ /* figure out if this is a read or a write */
+ if (dst_size && *dst_size)
+ {
+ /* dest buffer specified, must be a read */
+ /* assert (com->cdb_size == src_size); */
+ com->direction = scsi_read;
+ buf_ptr = dst;
+ com->buf_size = *dst_size;
+ }
+ else
+ {
+ /* no dest buffer, must be a write */
+ /* assert (com->cdb_size <= src_size); */
+ com->direction = scsi_write;
+ buf_ptr = (char *) src;
+ com->buf_size = src_size;
+ if (com->buf_size)
+ memcpy (fd_info[fd].pdata, buf_ptr, com->buf_size);
+ }
+
+ CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1;
+ ec2_$advance (&com->CommandAvailable, &status);
+ DomainErrorCheck (status, "Can't advance CommandAvailable EC");
+ CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1])
+ + DomainECWaitConstant);
+ index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2, &status);
+ DomainErrorCheck (status, "Error waiting on Enter command acceptance EC");
+ if (index != 1)
+ {
+ DBG (0, "Domain SANE Server never accepted Enter Command\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Read the result */
+ status = com->CommandStatus;
+ SCSIStatus = com->SCSIStatus;
+
+ /* Release the lock */
+ mutex_$unlock (&com->CommandLock);
+
+ /* Now decode the return status */
+ if (status.all)
+ DBG (1, "Server returned status %08x from Enter command\n", status.all);
+ switch (status.all)
+ {
+ case status_$ok:
+ sane_status = SANE_STATUS_GOOD;
+ break;
+ case scsi_$dma_underrun:
+ sane_status = SANE_STATUS_IO_ERROR;
+ /* This error is generated by the HP and UMAX backends. They
+ ask for too much data. For now, the error is ignored :-( */
+ sane_status = SANE_STATUS_GOOD;
+ break;
+ case scsi_$operation_timeout:
+ sane_status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ case scsi_$hdwr_failure: /* received when both scanners were active */
+ sane_status = SANE_STATUS_IO_ERROR;
+ break;
+ case (status_$ok | 0x80000000):
+ /* Special - no Domain/OS error, but fail bit set means to check
+ SCSI operation status. */
+ DBG (1, "Server returned SCSI status of %08x\n", SCSIStatus);
+ switch (SCSIStatus)
+ {
+ case scsi_check_condition:
+ /* Call the sense handler, if defined */
+ handler = fd_info[com->fd].sense_handler;
+ if (handler)
+ (*handler) (fd, ((u_char *) fd_info[fd].pdata
+ + DomainMaxDataSize),
+ fd_info[com->fd].sense_handler_arg);
+ sane_status = SANE_STATUS_IO_ERROR;
+ break;
+ case scsi_busy:
+ sane_status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ default:
+ DBG (0, "Error - Unrecognized SCSI status %08x returned from "
+ "Enter command\n", SCSIStatus);
+ sane_status = SANE_STATUS_IO_ERROR;
+ exit (EXIT_FAILURE);
+ }
+ break;
+ default:
+ DBG (0, "Unmapped status (%08x) returned from Domain SANE Server\n",
+ status.all);
+ sane_status = SANE_STATUS_IO_ERROR;
+ }
+
+ /* If a read, copy the data into the destination buffer */
+ if ((com->direction == scsi_read) && com->dst_size)
+ memcpy (buf_ptr, fd_info[fd].pdata, com->dst_size);
+
+ return sane_status;
+}
+
+
+SANE_Status
+sanei_scsi_req_wait (void *id)
+{
+ SANE_Status status;
+ DBG (1, "sanei_scsi_req_wait: (id=%p)\n", id);
+ status = SANE_STATUS_GOOD;
+ return status;
+}
+
+
+SANE_Status
+sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ SANE_Status status;
+ void *id;
+
+ DBG (1, "sanei_scsi_cmd2: (fd=%d)\n", fd);
+ status =
+ sanei_scsi_req_enter2 (fd, cmd, cmd_size, src, src_size, dst, dst_size,
+ &id);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return sanei_scsi_req_wait (id);
+}
+
+#endif /* USE == DOMAINOS_INTERFACE */
+
+
+#if USE == LINUX_INTERFACE
+
+#include <ctype.h>
+#include <signal.h>
+
+#include <sys/time.h>
+
+#define WE_HAVE_ASYNC_SCSI
+#define WE_HAVE_FIND_DEVICES
+
+static int pack_id = 0;
+static int need_init = 1;
+static sigset_t all_signals;
+
+#define ATOMIC(s) \
+do \
+ { \
+ sigset_t old_mask; \
+ \
+ if (need_init) \
+ { \
+ need_init = 0; \
+ sigfillset (&all_signals); \
+ } \
+ sigprocmask (SIG_BLOCK, &all_signals, &old_mask); \
+ {s;} \
+ sigprocmask (SIG_SETMASK, &old_mask, 0); \
+ } \
+while (0)
+
+static void
+issue (struct req *req)
+{
+ ssize_t nwritten;
+ fdparms *fdp;
+ struct req *rp;
+ int retries;
+ int ret;
+
+ if (!req)
+ return;
+
+ fdp = (fdparms *) fd_info[req->fd].pdata;
+ DBG (4, "sanei_scsi.issue: %p\n", (void *) req);
+
+ rp = fdp->sane_qhead;
+ while (rp && rp->running)
+ rp = rp->next;
+
+ while (rp && fdp->sg_queue_used < fdp->sg_queue_max)
+ {
+ retries = 20;
+ while (retries)
+ {
+ errno = 0;
+#ifdef SG_IO
+ if (sg_version < 30000)
+ {
+#endif
+ ATOMIC (rp->running = 1;
+ nwritten = write (rp->fd, &rp->sgdata.cdb,
+ rp->sgdata.cdb.hdr.pack_len);
+ ret = 0;
+ if (nwritten != rp->sgdata.cdb.hdr.pack_len)
+ {
+ /* ENOMEM can easily happen, if both command queueing
+ inside the SG driver and large buffers are used.
+ Therefore, if ENOMEM does not occur for the first
+ command in the queue, we simply try to issue
+ it later again.
+ */
+ if (errno == EAGAIN
+ || (errno == ENOMEM && rp != fdp->sane_qhead))
+ {
+ /* don't try to send the data again, but
+ wait for the next call to issue()
+ */
+ rp->running = 0;}
+ }
+ );
+#ifdef SG_IO
+ }
+ else
+ {
+ ATOMIC (rp->running = 1;
+ ret = ioctl(rp->fd, SG_IO, &rp->sgdata.sg3.hdr);
+ nwritten = 0;
+ if (ret < 0)
+ {
+ /* ENOMEM can easily happen, if both command queuein
+ inside the SG driver and large buffers are used.
+ Therefore, if ENOMEM does not occur for the first
+ command in the queue, we simply try to issue
+ it later again.
+ */
+ if (errno == EAGAIN
+ || (errno == ENOMEM && rp != fdp->sane_qhead))
+ {
+ /* don't try to send the data again, but
+ wait for the next call to issue()
+ */
+ rp->running = 0;
+ }
+ else /* game over */
+ {
+ rp->running = 0;
+ rp->done = 1;
+ rp->status = SANE_STATUS_IO_ERROR;
+ }
+ }
+ );
+ IF_DBG (if (DBG_LEVEL >= 255)
+ system ("cat /proc/scsi/sg/debug 1>&2");)
+ }
+#endif
+ if (rp == fdp->sane_qhead && errno == EAGAIN)
+ {
+ retries--;
+ usleep (10000);
+ }
+ else
+ retries = 0;
+ }
+
+#ifndef SG_IO
+ if (nwritten != rp->sgdata.cdb.hdr.pack_len)
+#else
+ if ((sg_version < 30000 && nwritten != rp->sgdata.cdb.hdr.pack_len)
+ || (sg_version >= 30000 && ret < 0))
+#endif
+ {
+ if (rp->running)
+ {
+#ifdef SG_IO
+ if (sg_version < 30000)
+#endif
+ DBG (1, "sanei_scsi.issue: bad write (errno=%i) %s %li\n",
+ errno, strerror (errno), (long)nwritten);
+#ifdef SG_IO
+ else if (sg_version > 30000)
+ DBG (1, "sanei_scsi.issue: SG_IO ioctl error (errno=%i, ret=%d) %s\n",
+ errno, ret, strerror (errno));
+#endif
+ rp->done = 1;
+ if (errno == ENOMEM)
+ {
+ DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "
+ "Check file PROBLEMS.\n");
+ rp->status = SANE_STATUS_NO_MEM;
+ }
+ else
+ rp->status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ if (errno == ENOMEM)
+ DBG (1, "issue: ENOMEM - cannot queue SCSI command. "
+ "Trying again later.\n");
+ else
+ DBG (1, "issue: EAGAIN - cannot queue SCSI command. "
+ "Trying again later.\n");
+ }
+ break; /* in case of an error don't try to queue more commands */
+ }
+ else
+ {
+#ifdef SG_IO
+ if (sg_version < 30000)
+#endif
+ req->status = SANE_STATUS_IO_ERROR;
+#ifdef SG_IO
+ else if (sg_version > 30000) /* SG_IO is synchronous, we're all set */
+ req->status = SANE_STATUS_GOOD;
+#endif
+ }
+ fdp->sg_queue_used++;
+ rp = rp->next;
+ }
+}
+
+ void sanei_scsi_req_flush_all_extended (int fd)
+ {
+ fdparms *fdp;
+ struct req *req, *next_req;
+ int len, count;
+
+ fdp = (fdparms *) fd_info[fd].pdata;
+ for (req = fdp->sane_qhead; req; req = next_req)
+ {
+ if (req->running && !req->done)
+ {
+ count = sane_scsicmd_timeout * 10;
+ while (count)
+ {
+ errno = 0;
+#ifdef SG_IO
+ if (sg_version < 30000)
+#endif
+ len =
+ read (fd, &req->sgdata.cdb,
+ req->sgdata.cdb.hdr.reply_len);
+#ifdef SG_IO
+ else
+ len = read (fd, &req->sgdata.sg3.hdr, sizeof (Sg_io_hdr));
+#endif
+ if (len >= 0 || (len < 0 && errno != EAGAIN))
+ break;
+ usleep (100000);
+ count--;
+ }
+ ((fdparms *) fd_info[req->fd].pdata)->sg_queue_used--;
+ }
+ next_req = req->next;
+
+ req->next = fdp->sane_free_list;
+ fdp->sane_free_list = req;
+ }
+
+ fdp->sane_qhead = fdp->sane_qtail = 0;
+ }
+
+ void sanei_scsi_req_flush_all ()
+ {
+ int fd, i, j = 0;
+
+ /* sanei_scsi_open allows only one open file handle, so we
+ can simply look for the first entry where in_use is set
+ */
+
+ fd = num_alloced;
+ for (i = 0; i < num_alloced; i++)
+ if (fd_info[i].in_use)
+ {
+ j++;
+ fd = i;
+ }
+
+ assert (j < 2);
+
+ if (fd < num_alloced)
+ sanei_scsi_req_flush_all_extended (fd);
+ }
+
+ SANE_Status
+ sanei_scsi_req_enter2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+ {
+ struct req *req;
+ size_t size;
+ fdparms *fdp;
+
+ fdp = (fdparms *) fd_info[fd].pdata;
+
+ if (fdp->sane_free_list)
+ {
+ req = fdp->sane_free_list;
+ fdp->sane_free_list = req->next;
+ req->next = 0;
+ }
+ else
+ {
+#ifdef SG_IO
+ if (sg_version < 30000)
+#endif
+ size = (sizeof (*req) - sizeof (req->sgdata.cdb.data)
+ + fdp->buffersize);
+#ifdef SG_IO
+ else
+ size = sizeof (*req) + MAX_CDB + fdp->buffersize
+ - sizeof (req->sgdata.sg3.data);
+#endif
+ req = malloc (size);
+ if (!req)
+ {
+ DBG (1, "sanei_scsi_req_enter: failed to malloc %lu bytes\n",
+ (u_long) size);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+ req->fd = fd;
+ req->running = 0;
+ req->done = 0;
+ req->status = SANE_STATUS_GOOD;
+ req->dst = dst;
+ req->dst_len = dst_size;
+#ifdef SG_IO
+ if (sg_version < 30000)
+ {
+#endif
+ memset (&req->sgdata.cdb.hdr, 0, sizeof (req->sgdata.cdb.hdr));
+ req->sgdata.cdb.hdr.pack_id = pack_id++;
+ req->sgdata.cdb.hdr.pack_len = cmd_size + src_size
+ + sizeof (req->sgdata.cdb.hdr);
+ req->sgdata.cdb.hdr.reply_len = (dst_size ? *dst_size : 0)
+ + sizeof (req->sgdata.cdb.hdr);
+ memcpy (&req->sgdata.cdb.data, cmd, cmd_size);
+ memcpy (&req->sgdata.cdb.data[cmd_size], src, src_size);
+ if (CDB_SIZE (*(const u_char *) cmd) != cmd_size)
+ {
+ if (ioctl (fd, SG_NEXT_CMD_LEN, &cmd_size))
+ {
+ DBG (1,
+ "sanei_scsi_req_enter2: ioctl to set command length failed\n");
+ }
+ }
+#ifdef SG_IO
+ }
+ else
+ {
+ memset (&req->sgdata.sg3.hdr, 0, sizeof (req->sgdata.sg3.hdr));
+ req->sgdata.sg3.hdr.interface_id = 'S';
+ req->sgdata.sg3.hdr.cmd_len = cmd_size;
+ req->sgdata.sg3.hdr.iovec_count = 0;
+ req->sgdata.sg3.hdr.mx_sb_len = SENSE_MAX;
+ /* read or write? */
+ if (dst_size && *dst_size)
+ {
+ req->sgdata.sg3.hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ req->sgdata.sg3.hdr.dxfer_len = *dst_size;
+ req->sgdata.sg3.hdr.dxferp = dst;
+ }
+ else if (src_size)
+ {
+ req->sgdata.sg3.hdr.dxfer_direction = SG_DXFER_TO_DEV;
+ if (src_size > fdp->buffersize)
+ {
+ DBG (1,
+ "sanei_scsi_req_enter2 warning: truncating write data "
+ "from requested %li bytes to allowed %li bytes\n",
+ (long)src_size, (long)fdp->buffersize);
+ src_size = fdp->buffersize;
+ }
+ req->sgdata.sg3.hdr.dxfer_len = src_size;
+ memcpy (&req->sgdata.sg3.data[MAX_CDB], src, src_size);
+ req->sgdata.sg3.hdr.dxferp = &req->sgdata.sg3.data[MAX_CDB];
+ }
+ else
+ {
+ req->sgdata.sg3.hdr.dxfer_direction = SG_DXFER_NONE;
+ }
+ if (cmd_size > MAX_CDB)
+ {
+ DBG (1, "sanei_scsi_req_enter2 warning: truncating write data "
+ "from requested %li bytes to allowed %i bytes\n",
+ (long)cmd_size, MAX_CDB);
+ cmd_size = MAX_CDB;
+ }
+ memcpy (req->sgdata.sg3.data, cmd, cmd_size);
+ req->sgdata.sg3.hdr.cmdp = req->sgdata.sg3.data;
+ req->sgdata.sg3.hdr.sbp = &(req->sgdata.sg3.sense_buffer[0]);
+ req->sgdata.sg3.hdr.timeout = 1000 * sane_scsicmd_timeout;
+#ifdef ENABLE_SCSI_DIRECTIO
+ /* for the adventurous: If direct IO is used,
+ the kernel locks the buffer. This can lead to conflicts,
+ if a backend uses shared memory.
+ OTOH, direct IO may be faster, and it reduces memory usage
+ */
+ req->sgdata.sg3.hdr.flags = SG_FLAG_DIRECT_IO;
+#else
+ req->sgdata.sg3.hdr.flags = 0;
+#endif
+ req->sgdata.sg3.hdr.pack_id = pack_id++;
+ req->sgdata.sg3.hdr.usr_ptr = 0;
+ }
+#endif
+
+ req->next = 0;
+ ATOMIC (if (fdp->sane_qtail)
+ {
+ fdp->sane_qtail->next = req; fdp->sane_qtail = req;}
+ else
+ fdp->sane_qhead = fdp->sane_qtail = req);
+
+ DBG (4, "scsi_req_enter: entered %p\n", (void *) req);
+
+ *idp = req;
+ issue (req);
+
+ DBG (10, "scsi_req_enter: queue_used: %i, queue_max: %i\n",
+ ((fdparms *) fd_info[fd].pdata)->sg_queue_used,
+ ((fdparms *) fd_info[fd].pdata)->sg_queue_max);
+
+ return SANE_STATUS_GOOD;
+ }
+
+ SANE_Status sanei_scsi_req_wait (void *id)
+ {
+ SANE_Status status = SANE_STATUS_GOOD;
+ struct req *req = id;
+ ssize_t nread = 0;
+
+ /* we don't support out-of-order completion */
+ assert (req == ((fdparms *) fd_info[req->fd].pdata)->sane_qhead);
+
+ DBG (4, "sanei_scsi_req_wait: waiting for %p\n", (void *) req);
+
+ issue (req); /* ensure the command is running */
+ if (req->done)
+ {
+ issue (req->next); /* issue next command, if any */
+ status = req->status;
+ }
+ else
+ {
+#ifdef SG_IO
+ if (sg_version < 30000)
+ {
+#endif
+ fd_set readable;
+
+ /* wait for command completion: */
+ FD_ZERO (&readable);
+ FD_SET (req->fd, &readable);
+ select (req->fd + 1, &readable, 0, 0, 0);
+
+ /* now atomically read result and set DONE: */
+ ATOMIC (nread = read (req->fd, &req->sgdata.cdb,
+ req->sgdata.cdb.hdr.reply_len);
+ req->done = 1);
+#ifdef SG_IO
+ }
+ else
+ {
+ IF_DBG (if (DBG_LEVEL >= 255)
+ system ("cat /proc/scsi/sg/debug 1>&2");)
+
+ /* set DONE: */
+ nread = 0; /* unused in this code path */
+ req->done = 1;
+ }
+#endif
+
+ if (fd_info[req->fd].pdata)
+ ((fdparms *) fd_info[req->fd].pdata)->sg_queue_used--;
+
+ /* Now issue next command asap, if any. We can't do this
+ earlier since the Linux kernel has space for just one big
+ buffer. */
+ issue (req->next);
+
+ DBG (4, "sanei_scsi_req_wait: read %ld bytes\n", (long) nread);
+
+ if (nread < 0)
+ {
+ DBG (1, "sanei_scsi_req_wait: read returned %ld (errno=%d)\n",
+ (long) nread, errno);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+#ifdef SG_IO
+ if (sg_version < 30000)
+ {
+#endif
+ nread -= sizeof (req->sgdata.cdb.hdr);
+
+ /* check for errors, but let the sense_handler decide.... */
+ if ((req->sgdata.cdb.hdr.result != 0) ||
+ (((req->sgdata.cdb.hdr.sense_buffer[0] & 0x7f) != 0)
+#ifdef HAVE_SG_TARGET_STATUS
+ /* this is messy... Sometimes it happens that we have
+ a valid looking sense buffer, but the DRIVER_SENSE
+ bit is not set. Moreover, we can check this only for
+ not tooo old SG drivers
+ */
+ && (req->sgdata.cdb.hdr.driver_status & DRIVER_SENSE)
+#endif
+ ))
+ {
+ SANEI_SCSI_Sense_Handler handler
+ = fd_info[req->fd].sense_handler;
+ void *arg = fd_info[req->fd].sense_handler_arg;
+
+ DBG (1,
+ "sanei_scsi_req_wait: SCSI command complained: %s\n",
+ strerror (req->sgdata.cdb.hdr.result));
+ DBG (10,
+ "sense buffer: %02x %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ req->sgdata.cdb.hdr.sense_buffer[0],
+ req->sgdata.cdb.hdr.sense_buffer[1],
+ req->sgdata.cdb.hdr.sense_buffer[2],
+ req->sgdata.cdb.hdr.sense_buffer[3],
+ req->sgdata.cdb.hdr.sense_buffer[4],
+ req->sgdata.cdb.hdr.sense_buffer[5],
+ req->sgdata.cdb.hdr.sense_buffer[6],
+ req->sgdata.cdb.hdr.sense_buffer[7],
+ req->sgdata.cdb.hdr.sense_buffer[8],
+ req->sgdata.cdb.hdr.sense_buffer[9],
+ req->sgdata.cdb.hdr.sense_buffer[10],
+ req->sgdata.cdb.hdr.sense_buffer[11],
+ req->sgdata.cdb.hdr.sense_buffer[12],
+ req->sgdata.cdb.hdr.sense_buffer[13],
+ req->sgdata.cdb.hdr.sense_buffer[14],
+ req->sgdata.cdb.hdr.sense_buffer[15]);
+#ifdef HAVE_SG_TARGET_STATUS
+ /* really old SG header do not define target_status,
+ host_status and driver_status
+ */
+ DBG (10, "target status: %02x host status: %02x"
+ " driver status: %02x\n",
+ req->sgdata.cdb.hdr.target_status,
+ req->sgdata.cdb.hdr.host_status,
+ req->sgdata.cdb.hdr.driver_status);
+
+ if (req->sgdata.cdb.hdr.host_status == DID_NO_CONNECT || req->sgdata.cdb.hdr.host_status == DID_BUS_BUSY || req->sgdata.cdb.hdr.host_status == DID_TIME_OUT || req->sgdata.cdb.hdr.driver_status == DRIVER_BUSY || req->sgdata.cdb.hdr.target_status == 0x04) /* BUSY */
+#else
+ if (req->sgdata.cdb.hdr.result == EBUSY)
+#endif
+ status = SANE_STATUS_DEVICE_BUSY;
+ else if (handler)
+ /* sense handler should return SANE_STATUS_GOOD if it
+ decided all was ok afterall */
+ status =
+ (*handler) (req->fd, req->sgdata.cdb.hdr.sense_buffer,
+ arg);
+ else
+ status = SANE_STATUS_IO_ERROR;
+ }
+
+ /* if we are ok so far, copy over the return data */
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (req->dst)
+ memcpy (req->dst, req->sgdata.cdb.data, nread);
+
+ if (req->dst_len)
+ *req->dst_len = nread;
+ }
+#ifdef SG_IO
+ }
+ else
+ {
+ /* check for errors, but let the sense_handler decide.... */
+ if (((req->sgdata.sg3.hdr.info & SG_INFO_CHECK) != 0)
+ || ((req->sgdata.sg3.hdr.sb_len_wr > 0)
+ && ((req->sgdata.sg3.sense_buffer[0] & 0x7f) != 0)
+ && (req->sgdata.sg3.hdr.
+ driver_status & DRIVER_SENSE)))
+ {
+ SANEI_SCSI_Sense_Handler handler
+ = fd_info[req->fd].sense_handler;
+ void *arg = fd_info[req->fd].sense_handler_arg;
+
+ DBG (1,
+ "sanei_scsi_req_wait: SCSI command complained: %s\n",
+ strerror (errno));
+ DBG (10,
+ "sense buffer: %02x %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ req->sgdata.sg3.sense_buffer[0],
+ req->sgdata.sg3.sense_buffer[1],
+ req->sgdata.sg3.sense_buffer[2],
+ req->sgdata.sg3.sense_buffer[3],
+ req->sgdata.sg3.sense_buffer[4],
+ req->sgdata.sg3.sense_buffer[5],
+ req->sgdata.sg3.sense_buffer[6],
+ req->sgdata.sg3.sense_buffer[7],
+ req->sgdata.sg3.sense_buffer[8],
+ req->sgdata.sg3.sense_buffer[9],
+ req->sgdata.sg3.sense_buffer[10],
+ req->sgdata.sg3.sense_buffer[11],
+ req->sgdata.sg3.sense_buffer[12],
+ req->sgdata.sg3.sense_buffer[13],
+ req->sgdata.sg3.sense_buffer[14],
+ req->sgdata.sg3.sense_buffer[15]);
+ DBG (10,
+ "target status: %02x host status: %04x"
+ " driver status: %04x\n", req->sgdata.sg3.hdr.status,
+ req->sgdata.sg3.hdr.host_status,
+ req->sgdata.sg3.hdr.driver_status);
+
+ /* the first three tests below are an replacement of the
+ error "classification" as it was with the old SG driver,
+ the fourth test is new.
+ */
+ if (req->sgdata.sg3.hdr.host_status == SG_ERR_DID_NO_CONNECT || req->sgdata.sg3.hdr.host_status == SG_ERR_DID_BUS_BUSY || req->sgdata.sg3.hdr.host_status == SG_ERR_DID_TIME_OUT || req->sgdata.sg3.hdr.driver_status == DRIVER_BUSY || req->sgdata.sg3.hdr.masked_status == 0x04) /* BUSY */
+ status = SANE_STATUS_DEVICE_BUSY;
+ else if (handler && req->sgdata.sg3.hdr.sb_len_wr)
+ /* sense handler should return SANE_STATUS_GOOD if it
+ decided all was ok afterall */
+ status =
+ (*handler) (req->fd, req->sgdata.sg3.sense_buffer,
+ arg);
+
+ /* status bits INTERMEDIATE and CONDITION MET should not
+ result in an error; neither should reserved bits
+ */
+ else if (((req->sgdata.sg3.hdr.status & 0x2a) == 0)
+ && (req->sgdata.sg3.hdr.host_status ==
+ SG_ERR_DID_OK)
+ &&
+ ((req->sgdata.sg3.hdr.
+ driver_status & ~SG_ERR_DRIVER_SENSE) ==
+ SG_ERR_DRIVER_OK))
+ status = SANE_STATUS_GOOD;
+ else
+ status = SANE_STATUS_IO_ERROR;
+ }
+
+#if 0
+ /* Sometimes the Linux SCSI system reports bogus resid values.
+ Observed with lk 2.4.5, 2.4.13, aic7xxx and sym53c8xx drivers,
+ if command queueing is used. So we better issue only a warning
+ */
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (req->dst_len)
+ {
+ *req->dst_len -= req->sgdata.sg3.hdr.resid;
+ }
+ }
+#endif
+ if (req->sgdata.sg3.hdr.resid)
+ {
+ DBG (1,
+ "sanei_scsi_req_wait: SG driver returned resid %i\n",
+ req->sgdata.sg3.hdr.resid);
+ DBG (1,
+ " NOTE: This value may be bogus\n");
+ }
+ }
+#endif
+ }
+ }
+
+ /* dequeue and release processed request: */
+ ATOMIC (((fdparms *) fd_info[req->fd].pdata)->sane_qhead
+ = ((fdparms *) fd_info[req->fd].pdata)->sane_qhead->next;
+ if (!((fdparms *) fd_info[req->fd].pdata)->sane_qhead)
+ ((fdparms *) fd_info[req->fd].pdata)->sane_qtail = 0;
+ req->next = ((fdparms *) fd_info[req->fd].pdata)->sane_free_list;
+ ((fdparms *) fd_info[req->fd].pdata)->sane_free_list = req);
+ return status;
+ }
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ SANE_Status status;
+ void *id;
+
+ status =
+ sanei_scsi_req_enter2 (fd, cmd, cmd_size, src, src_size, dst, dst_size,
+ &id);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return sanei_scsi_req_wait (id);
+ }
+
+/* The following code (up to and including sanei_scsi_find_devices() )
+ is trying to match device/manufacturer names and/or SCSI addressing
+ numbers (i.e. <host,bus,id,lun>) with a sg device file name
+ (e.g. /dev/sg3).
+*/
+#define PROCFILE "/proc/scsi/scsi"
+#define DEVFS_MSK "/dev/scsi/host%d/bus%d/target%d/lun%d/generic"
+#define SCAN_MISSES 5
+
+/* Some <scsi/scsi.h> headers don't have the following define */
+#ifndef SCSI_IOCTL_GET_IDLUN
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+#endif
+
+ static int lx_sg_dev_base = -1;
+ static int lx_devfs = -1;
+
+ static const struct lx_device_name_list_tag
+ {
+ const char *prefix;
+ char base;
+ }
+ lx_dnl[] =
+ {
+ {
+ "/dev/sg", 0}
+ ,
+ {
+ "/dev/sg", 'a'}
+ ,
+ {
+ "/dev/uk", 0}
+ ,
+ {
+ "/dev/gsc", 0}
+ };
+
+ static int /* Returns open sg file descriptor, or -1 for no access,
+ or -2 for not found (or other error) */
+ lx_mk_devicename (int guess_devnum, char *name, size_t name_len)
+ {
+ int dev_fd, k, dnl_len;
+ const struct lx_device_name_list_tag *dnp;
+
+ dnl_len = NELEMS (lx_dnl);
+ k = ((-1 == lx_sg_dev_base) ? 0 : lx_sg_dev_base);
+ for (; k < dnl_len; ++k)
+ {
+ dnp = &lx_dnl[k];
+ if (dnp->base)
+ snprintf (name, name_len, "%s%c", dnp->prefix,
+ dnp->base + guess_devnum);
+ else
+ snprintf (name, name_len, "%s%d", dnp->prefix, guess_devnum);
+ dev_fd = -1;
+#ifdef HAVE_RESMGR
+ dev_fd = rsm_open_device (name, O_RDWR | O_NONBLOCK);
+#endif
+ if (dev_fd == -1)
+ dev_fd = open (name, O_RDWR | O_NONBLOCK);
+ if (dev_fd >= 0)
+ {
+ lx_sg_dev_base = k;
+ return dev_fd;
+ }
+ else if ((EACCES == errno) || (EBUSY == errno))
+ {
+ lx_sg_dev_base = k;
+ return -1;
+ }
+ if (-1 != lx_sg_dev_base)
+ return -2;
+ }
+ return -2;
+ }
+
+ static int /* Returns 1 for match, else 0 */
+ lx_chk_id (int dev_fd, int host, int channel, int id, int lun)
+ {
+#ifdef SG_GET_SCSI_ID_FOUND
+ struct sg_scsi_id ssid;
+
+ if ((ioctl (dev_fd, SG_GET_SCSI_ID, &ssid) >= 0))
+ {
+ DBG (2, "lx_chk_id: %d,%d %d,%d %d,%d %d,%d\n", host, ssid.host_no,
+ channel, ssid.channel, id, ssid.scsi_id, lun, ssid.lun);
+ if ((host == ssid.host_no) &&
+ (channel == ssid.channel) &&
+ (id == ssid.scsi_id) && (lun == ssid.lun))
+ return 1;
+ else
+ return 0;
+ }
+#endif
+ {
+ struct my_scsi_idlun
+ {
+ int dev_id;
+ int host_unique_id;
+ }
+ my_idlun;
+ if (ioctl (dev_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun) >= 0)
+ {
+ if (((my_idlun.dev_id & 0xff) == id) &&
+ (((my_idlun.dev_id >> 8) & 0xff) == lun) &&
+ (((my_idlun.dev_id >> 16) & 0xff) == channel))
+ return 1; /* cheating, assume 'host' number matches */
+ }
+ }
+ return 0;
+ }
+
+ static int /* Returns 1 if match with 'name' set, else 0 */
+
+ lx_scan_sg (int exclude_devnum, char *name, size_t name_len,
+ int host, int channel, int id, int lun)
+ {
+ int dev_fd, k, missed;
+
+ if (-1 == lx_sg_dev_base)
+ return 0;
+ for (k = 0, missed = 0; (missed < SCAN_MISSES) && (k < 255);
+ ++k, ++missed)
+ {
+ DBG (2, "lx_scan_sg: k=%d, exclude=%d, missed=%d\n", k,
+ exclude_devnum, missed);
+ if (k == exclude_devnum)
+ {
+ missed = 0;
+ continue; /* assumed this one has been checked already */
+ }
+ if ((dev_fd = lx_mk_devicename (k, name, name_len)) >= 0)
+ {
+ missed = 0;
+ if (lx_chk_id (dev_fd, host, channel, id, lun))
+ {
+ close (dev_fd);
+ return 1;
+ }
+ close (dev_fd);
+ }
+ else if (-1 == dev_fd)
+ missed = 0; /* no permissions but something found */
+ }
+ return 0;
+ }
+
+ static int /* Returns 1 if match, else 0 */
+
+ lx_chk_devicename (int guess_devnum, char *name, size_t name_len,
+ int host, int channel, int id, int lun)
+ {
+ int dev_fd;
+
+ if (host < 0)
+ return 0;
+ if (0 != lx_devfs)
+ { /* simple mapping if we have devfs */
+ if (-1 == lx_devfs)
+ {
+ if ((dev_fd =
+ lx_mk_devicename (guess_devnum, name, name_len)) >= 0)
+ close (dev_fd); /* hack to load sg driver module */
+ }
+ snprintf (name, name_len, DEVFS_MSK, host, channel, id, lun);
+ dev_fd = open (name, O_RDWR | O_NONBLOCK);
+ if (dev_fd >= 0)
+ {
+ close (dev_fd);
+ lx_devfs = 1;
+ DBG (1, "lx_chk_devicename: matched device(devfs): %s\n", name);
+ return 1;
+ }
+ else if (ENOENT == errno)
+ lx_devfs = 0;
+ }
+
+ if ((dev_fd = lx_mk_devicename (guess_devnum, name, name_len)) < -1)
+ { /* no candidate sg device file name found, try /dev/sg0,1 */
+ if ((dev_fd = lx_mk_devicename (0, name, name_len)) < -1)
+ {
+ if ((dev_fd = lx_mk_devicename (1, name, name_len)) < -1)
+ return 0; /* no luck finding sg fd to open */
+ }
+ }
+ if (dev_fd >= 0)
+ {
+/* now check this fd for match on <host, channel, id, lun> */
+ if (lx_chk_id (dev_fd, host, channel, id, lun))
+ {
+ close (dev_fd);
+ DBG (1, "lx_chk_devicename: matched device(direct): %s\n", name);
+ return 1;
+ }
+ close (dev_fd);
+ }
+/* if mismatch then call scan algorithm */
+ if (lx_scan_sg (guess_devnum, name, name_len, host, channel, id, lun))
+ {
+ DBG (1, "lx_chk_devicename: matched device(scan): %s\n", name);
+ return 1;
+ }
+ return 0;
+ }
+
+/* Legacy /proc/scsi/scsi */
+static void /* calls 'attach' function pointer with sg device file name iff match */
+sanei_proc_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+#define FOUND_VENDOR 1
+#define FOUND_MODEL 2
+#define FOUND_TYPE 4
+#define FOUND_REV 8
+#define FOUND_HOST 16
+#define FOUND_CHANNEL 32
+#define FOUND_ID 64
+#define FOUND_LUN 128
+#define FOUND_ALL 255
+
+ char *me = "sanei_proc_scsi_find_devices";
+
+ size_t findvendor_len = 0, findmodel_len = 0, findtype_len = 0;
+ char vendor[32], model[32], type[32], revision[32];
+ int bus, channel, id, lun;
+
+ int number, i, j, definedd;
+ char line[256], dev_name[128], *c1, *c2, ctmp;
+ const char *string;
+ FILE *proc_fp;
+ char *end;
+ struct
+ {
+ const char *name;
+ size_t name_len;
+ int is_int; /* integer valued? (not a string) */
+ union
+ {
+ void *v; /* avoids compiler warnings... */
+ char *str;
+ int *i;
+ }
+ u;
+ }
+ param[] =
+ {
+ {
+ "Vendor:", 7, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "Model:", 6, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "Type:", 5, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "Rev:", 4, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "scsi", 4, 1,
+ {
+ 0}
+ }
+ ,
+ {
+ "Channel:", 8, 1,
+ {
+ 0}
+ }
+ ,
+ {
+ "Id:", 3, 1,
+ {
+ 0}
+ }
+ ,
+ {
+ "Lun:", 4, 1,
+ {
+ 0}
+ }
+ };
+
+ param[0].u.str = vendor;
+ param[1].u.str = model;
+ param[2].u.str = type;
+ param[3].u.str = revision;
+ param[4].u.i = &bus;
+ param[5].u.i = &channel;
+ param[6].u.i = &id;
+ param[7].u.i = &lun;
+
+ DBG_INIT ();
+
+ proc_fp = fopen (PROCFILE, "r");
+ if (!proc_fp)
+ {
+ DBG (1, "%s: could not open %s for reading\n", me, PROCFILE);
+ return;
+ }
+
+ number = bus = channel = id = lun = -1;
+
+ vendor[0] = model[0] = type[0] = '\0';
+ if (findvendor)
+ findvendor_len = strlen (findvendor);
+ if (findmodel)
+ findmodel_len = strlen (findmodel);
+ if (findtype)
+ findtype_len = strlen (findtype);
+
+ definedd = 0;
+ while (!feof (proc_fp))
+ {
+ fgets (line, sizeof (line), proc_fp);
+ string = sanei_config_skip_whitespace (line);
+
+ while (*string)
+ {
+ for (i = 0; i < NELEMS (param); ++i)
+ {
+ if (strncmp (string, param[i].name, param[i].name_len) == 0)
+ {
+ string += param[i].name_len;
+ /* Make sure that we don't read the next parameter name
+ as a value, if the real value consists only of spaces
+ */
+ c2 = string + strlen (string);
+ for (j = 0; j < NELEMS (param); ++j)
+ {
+ c1 = strstr (string, param[j].name);
+ if ((j != i) && c1 && (c1 < c2))
+ c2 = c1;
+ }
+ ctmp = *c2;
+ *c2 = 0;
+ string = sanei_config_skip_whitespace (string);
+
+ if (param[i].is_int)
+ {
+ if (*string)
+ {
+ *param[i].u.i = strtol (string, &end, 10);
+ string = (char *) end;
+ }
+ else
+ *param[i].u.i = 0;
+ }
+ else
+ {
+ strncpy (param[i].u.str, string, 32);
+ param[i].u.str[31] = '\0';
+ /* while (*string && !isspace (*string))
+ ++string;
+ */
+ }
+ /* string = sanei_config_skip_whitespace (string); */
+ *c2 = ctmp;
+ string = c2;
+ definedd |= 1 << i;
+
+ if (param[i].u.v == &bus)
+ {
+ ++number;
+ definedd = FOUND_HOST;
+ }
+ break;
+ }
+ }
+ if (i >= NELEMS (param))
+ ++string; /* no match */
+ }
+
+ if (FOUND_ALL != definedd)
+ /* some info is still missing */
+ continue;
+
+ definedd = 0;
+ if ((!findvendor || strncmp (vendor, findvendor, findvendor_len) == 0)
+ && (!findmodel || strncmp (model, findmodel, findmodel_len) == 0)
+ && (!findtype || strncmp (type, findtype, findtype_len) == 0)
+ && (findbus == -1 || bus == findbus)
+ && (findchannel == -1 || channel == findchannel)
+ && (findid == -1 || id == findid)
+ && (findlun == -1 || lun == findlun))
+ {
+ DBG (2, "%s: found: vendor=%s model=%s type=%s\n\t"
+ "bus=%d chan=%d id=%d lun=%d num=%d\n",
+ me, findvendor, findmodel, findtype,
+ bus, channel, id, lun, number);
+ if (lx_chk_devicename (number, dev_name, sizeof (dev_name), bus,
+ channel, id, lun)
+ && ((*attach) (dev_name) != SANE_STATUS_GOOD))
+ {
+ DBG(1,"sanei_scsi_find_devices: bad attach\n");
+ }
+ }
+ else
+ {
+ DBG (2, "%s: no match\n", me);
+ }
+ vendor[0] = model[0] = type[0] = 0;
+ bus = channel = id = lun = -1;
+ }
+ fclose (proc_fp);
+ }
+
+#define SYSFS_SCSI_DEVICES "/sys/bus/scsi/devices"
+
+/* From linux/drivers/scsi/scsi.c */
+static char *lnxscsi_device_types[] = {
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications ",
+ "ASC IT8 ",
+ "ASC IT8 ",
+ "RAID ",
+ "Enclosure ",
+ "Direct-Access-RBC",
+ "Optical card ",
+ "Bridge controller",
+ "Object storage ",
+ "Automation/Drive "
+};
+
+void /* calls 'attach' function pointer with sg device file name iff match */
+sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ char *me = "sanei_scsi_find_devices";
+ char path[PATH_MAX];
+ char dev_name[128];
+ struct dirent buf;
+ struct dirent *de;
+ DIR *scsidevs;
+ FILE *fp;
+ char *ptr;
+ char *end;
+ int bcil[4]; /* bus, channel, id, lun */
+ char vmt[3][33]; /* vendor, model, type */
+ int vmt_len[3];
+ char *vmtfiles[3] = { "vendor", "model", "type" };
+ int lastbus;
+ int number;
+ int i;
+ long val;
+ int ret;
+
+ DBG_INIT ();
+
+ DBG (2, "%s: looking for: v=%s m=%s t=%s b=%d c=%d i=%d l=%d\n",
+ me, findvendor, findmodel, findtype,
+ findbus, findchannel, findid, findlun);
+
+ scsidevs = opendir (SYSFS_SCSI_DEVICES);
+ if (!scsidevs)
+ {
+ DBG (1, "%s: could not open %s; falling back to /proc\n",
+ me, SYSFS_SCSI_DEVICES);
+
+ sanei_proc_scsi_find_devices (findvendor, findmodel, findtype,
+ findbus, findchannel, findid, findlun,
+ attach);
+ return;
+ }
+
+ vmt_len[0] = (findvendor) ? strlen(findvendor) : 0;
+ vmt_len[1] = (findmodel) ? strlen(findmodel) : 0;
+ vmt_len[2] = (findtype) ? strlen(findtype) : 0;
+
+ lastbus = -1;
+ number = -1;
+ for (;;)
+ {
+ ret = readdir_r(scsidevs, &buf, &de);
+ if (ret != 0)
+ {
+ DBG (1, "%s: could not read directory %s: %s\n",
+ me, SYSFS_SCSI_DEVICES, strerror(errno));
+
+ break;
+ }
+
+ if (de == NULL)
+ break;
+
+ if (buf.d_name[0] == '.')
+ continue;
+
+ /* Extract bus, channel, id, lun from directory name b:c:i:l */
+ ptr = buf.d_name;
+ for (i = 0; i < 4; i++)
+ {
+ errno = 0;
+ val = strtol (ptr, &end, 10);
+ if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN)))
+ || ((errno != 0) && (val == 0)))
+ {
+ DBG (1, "%s: invalid integer in string (%s): %s\n",
+ me, ptr, strerror(errno));
+
+ i = 12; /* Skip */
+ break;
+ }
+
+ if (end == ptr)
+ {
+ DBG (1, "%s: no integer found in string: %s (%d)\n", me, ptr, i);
+
+ i = 12; /* Skip */
+ break;
+ }
+
+ if (*end && (*end != ':'))
+ {
+ DBG (1, "%s: parse error on string %s (%d)\n", me, buf.d_name, i);
+
+ i = 12; /* Skip */
+ break;
+ }
+
+ if (val > INT_MAX)
+ {
+ DBG (1, "%s: integer value too large (%s)\n", me, buf.d_name);
+
+ i = 12; /* Skip */
+ break;
+ }
+
+ bcil[i] = (int) val;
+ ptr = end + 1;
+ }
+
+ /* Skip this one */
+ if (i == 12)
+ continue;
+
+ if (bcil[0] != lastbus)
+ {
+ lastbus = bcil[0];
+ number++;
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ ret = snprintf (path, PATH_MAX, "%s/%s/%s",
+ SYSFS_SCSI_DEVICES, buf.d_name, vmtfiles[i]);
+ if ((ret < 0) || (ret >= PATH_MAX))
+ {
+ DBG (1, "%s: skipping %s/%s, PATH_MAX exceeded on %s\n",
+ me, SYSFS_SCSI_DEVICES, buf.d_name, vmtfiles[i]);
+
+ i = 12; /* Skip */
+ break;
+ }
+
+ memset (vmt[i], 0, sizeof(vmt[i]));
+
+ fp = fopen(path, "r");
+ if (!fp)
+ {
+ DBG (1, "%s: could not open %s: %s\n", me, path, strerror(errno));
+
+ i = 12; /* Skip */
+ break;
+ }
+
+ ret = fread (vmt[i], 1, sizeof(vmt[i]) - 1, fp);
+ if (ret <= 0)
+ {
+ if (ferror(fp))
+ {
+ DBG (1, "%s: error reading %s\n", me, path);
+
+ i = 12; /* Skip */
+ break;
+ }
+ }
+
+ if (vmt[i][ret - 1] == '\n')
+ vmt[i][ret - 1] = '\0';
+
+ fclose (fp);
+ }
+
+ /* Skip this one */
+ if (i == 12)
+ continue;
+
+ /* Type is a numeric string and must be converted back to a well-known string */
+ errno = 0;
+ val = strtol (vmt[2], &end, 10);
+ if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN)))
+ || ((errno != 0) && (val == 0)))
+ {
+ DBG (1, "%s: invalid integer in type string (%s): %s\n",
+ me, vmt[2], strerror(errno));
+ continue;
+ }
+
+ if (end == vmt[2])
+ {
+ DBG (1, "%s: no integer found in type string: %s\n", me, vmt[2]);
+ continue;
+ }
+
+ if ((val < 0) || (val >= (int)(sizeof(lnxscsi_device_types) / sizeof(lnxscsi_device_types[0]))))
+ {
+ DBG (1, "%s: invalid type %ld\n", me, val);
+ continue;
+ }
+
+ strncpy(vmt[2], lnxscsi_device_types[val], sizeof(vmt[2]) - 1);
+
+ if ((!findvendor || strncmp (vmt[0], findvendor, vmt_len[0]) == 0)
+ && (!findmodel || strncmp (vmt[1], findmodel, vmt_len[1]) == 0)
+ && (!findtype || strncmp (vmt[2], findtype, vmt_len[2]) == 0)
+ && (findbus == -1 || bcil[0] == findbus)
+ && (findchannel == -1 || bcil[1] == findchannel)
+ && (findid == -1 || bcil[2] == findid)
+ && (findlun == -1 || bcil[3] == findlun))
+ {
+ DBG (2, "%s: found: vendor=%s model=%s type=%s\n\t"
+ "bus=%d chan=%d id=%d lun=%d num=%d\n",
+ me, vmt[0], vmt[1], vmt[2],
+ bcil[0], bcil[1], bcil[2], bcil[3], number);
+
+ if (lx_chk_devicename (number, dev_name, sizeof (dev_name),
+ bcil[0], bcil[1], bcil[2], bcil[3])
+ && ((*attach) (dev_name) != SANE_STATUS_GOOD))
+ {
+ DBG (1, "%s: bad attach\n", me);
+ }
+ }
+ else
+ {
+ DBG (2, "%s: no match\n", me);
+ }
+ }
+
+ closedir(scsidevs);
+ }
+
+#endif /* USE == LINUX_INTERFACE */
+
+
+#if USE == BSD_INTERFACE
+
+#ifndef HAVE_SCSIREQ_ENTER
+ static int scsireq_enter (int fd, scsireq_t * hdr)
+ {
+ return ioctl (fd, SCIOCCOMMAND, hdr);
+ }
+#endif /* !HAVE_SCSIREQ_ENTER */
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ /* xxx obsolete: size_t cdb_size;
+ */
+ scsireq_t hdr;
+ int result;
+
+/* xxx obsolete:
+ cdb_size = CDB_SIZE (*(u_char *) src);
+*/
+
+ memset (&hdr, 0, sizeof (hdr));
+ memcpy (hdr.cmd, cmd, cmd_size);
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete: assert (cdb_size == src_size);
+ */
+ hdr.flags = SCCMD_READ;
+ hdr.databuf = dst;
+ hdr.datalen = *dst_size;
+ }
+ else
+ {
+ /* xxx obsolete: assert (cdb_size <= src_size);
+ */
+ hdr.flags = SCCMD_WRITE;
+ /* The old variant:
+ hdr.databuf = (char *) src + cdb_size;
+ hdr.datalen = src_size;
+ xxxxxx huh? Shouldn´t the above line have been src_size - cdb_size)
+ */
+ hdr.databuf = (char *) src;
+ hdr.datalen = src_size;
+ }
+ hdr.timeout = sane_scsicmd_timeout * 1000;
+ hdr.cmdlen = cmd_size;
+ hdr.senselen = sizeof (hdr.sense);
+
+ result = scsireq_enter (fd, &hdr);
+ if (result < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: scsi_reqenter() failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (hdr.retsts != SCCMD_OK)
+ {
+ SANEI_SCSI_Sense_Handler handler;
+
+ DBG (1, "sanei_scsi_cmd: scsi returned with status %d\n", hdr.retsts);
+ switch (hdr.retsts)
+ {
+ case SCCMD_TIMEOUT:
+ case SCCMD_BUSY:
+ return SANE_STATUS_DEVICE_BUSY;
+
+ case SCCMD_SENSE:
+ handler = fd_info[fd].sense_handler;
+ if (handler)
+ return (*handler) (fd, &hdr.sense[0],
+ fd_info[fd].sense_handler_arg);
+ /* fall through */
+ default:
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (dst_size)
+ *dst_size = hdr.datalen_used;
+
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == BSD_INTERFACE */
+
+#if USE == FREEBSD_CAM_INTERFACE
+ SANE_Status sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+
+ struct cam_device *dev;
+ union ccb *ccb;
+ int rv;
+ u_int32_t ccb_flags;
+ char *data_buf;
+ size_t data_len;
+ SANE_Status status;
+
+ if (fd < 0 || fd > CAM_MAXDEVS || cam_devices[fd] == NULL)
+ {
+ fprintf (stderr, "attempt to reference invalid unit %d\n", fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev = cam_devices[fd];
+ ccb = cam_getccb (dev);
+
+ /* Build the CCB */
+ bzero (&(&ccb->ccb_h)[1], sizeof (struct ccb_scsiio));
+ bcopy (cmd, &ccb->csio.cdb_io.cdb_bytes, cmd_size);
+
+ /*
+ * Set the data direction flags.
+ */
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete: assert (cdb_size == src_size);
+ */
+ ccb_flags = CAM_DIR_IN;
+ data_buf = ((char *) (dst));
+ data_len = *dst_size;
+ }
+ else if (src_size > 0)
+ {
+ ccb_flags = CAM_DIR_OUT;
+ data_buf = ((char *) (src));
+ data_len = src_size;
+ }
+ else
+ {
+ ccb_flags = CAM_DIR_NONE;
+ data_buf = NULL;
+ data_len = 0;
+ }
+
+ cam_fill_csio (&ccb->csio,
+ /* retries */ 1,
+ /* cbfncp */ NULL,
+ /* flags */ ccb_flags,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* data_ptr */ (u_int8_t *) data_buf,
+ /* dxfer_len */ data_len,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* cdb_len */ cmd_size,
+ /* timeout */ sane_scsicmd_timeout * 1000);
+
+ /* Run the command */
+ errno = 0;
+ if ((rv = cam_send_ccb (dev, ccb)) == -1)
+ {
+ cam_freeccb (ccb);
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ {
+ SANEI_SCSI_Sense_Handler handler;
+
+ DBG (1, "sanei_scsi_cmd: scsi returned with status %d\n",
+ (ccb->ccb_h.status & CAM_STATUS_MASK));
+
+ switch (ccb->ccb_h.status & CAM_STATUS_MASK)
+ {
+ case CAM_BUSY:
+ case CAM_SEL_TIMEOUT:
+ case CAM_SCSI_BUSY:
+ status = SANE_STATUS_DEVICE_BUSY;
+ break;
+ default:
+ status = SANE_STATUS_IO_ERROR;
+ }
+
+ handler = fd_info[fd].sense_handler;
+ if (handler && (ccb->ccb_h.status & CAM_AUTOSNS_VALID))
+ {
+ SANE_Status st = (*handler)
+ (fd, ((u_char *) (&ccb->csio.sense_data)),
+ fd_info[fd].sense_handler_arg);
+ cam_freeccb (ccb);
+ return st;
+ }
+ else
+ {
+ cam_freeccb (ccb);
+ return status;
+ }
+ }
+ cam_freeccb (ccb);
+ return SANE_STATUS_GOOD;
+ }
+
+#define WE_HAVE_FIND_DEVICES
+
+ int
+ cam_compare_inquiry (int fd, path_id_t path_id,
+ target_id_t target_id, lun_id_t target_lun,
+ const char *vendor, const char *product,
+ const char *type)
+ {
+ struct ccb_dev_match cdm;
+ struct device_match_pattern *pattern;
+ struct scsi_inquiry_data *inq;
+ int retval = 0;
+
+ /* build ccb for device match */
+ bzero (&cdm, sizeof (cdm));
+ cdm.ccb_h.func_code = XPT_DEV_MATCH;
+
+ /* result buffer */
+ cdm.match_buf_len = sizeof (struct dev_match_result);
+ cdm.matches = (struct dev_match_result *) malloc (cdm.match_buf_len);
+ cdm.num_matches = 0;
+
+ /* pattern buffer */
+ cdm.num_patterns = 1;
+ cdm.pattern_buf_len = sizeof (struct dev_match_pattern);
+ cdm.patterns = (struct dev_match_pattern *) malloc (cdm.pattern_buf_len);
+
+ /* assemble conditions */
+ cdm.patterns[0].type = DEV_MATCH_DEVICE;
+ pattern = &cdm.patterns[0].pattern.device_pattern;
+ pattern->flags = DEV_MATCH_PATH | DEV_MATCH_TARGET | DEV_MATCH_LUN;
+ pattern->path_id = path_id;
+ pattern->target_id = target_id;
+ pattern->target_lun = target_lun;
+
+ if (ioctl (fd, CAMIOCOMMAND, &cdm) == -1)
+ {
+ DBG (1, "error sending CAMIOCOMMAND ioctl");
+ retval = -1;
+ goto ret;
+ }
+
+ if ((cdm.ccb_h.status != CAM_REQ_CMP)
+ || ((cdm.status != CAM_DEV_MATCH_LAST)
+ && (cdm.status != CAM_DEV_MATCH_MORE)))
+ {
+ DBG (1, "got CAM error %#x, CDM error %d\n",
+ cdm.ccb_h.status, cdm.status);
+ retval = -1;
+ goto ret;
+ }
+
+ if (cdm.num_matches == 0)
+ {
+ DBG (1, "not found\n");
+ retval = -1;
+ goto ret;
+ }
+
+ if (cdm.matches[0].type != DEV_MATCH_DEVICE)
+ {
+ DBG (1, "no device match\n");
+ retval = -1;
+ goto ret;
+ }
+
+ inq = &cdm.matches[0].result.device_result.inq_data;
+ if ((vendor && cam_strmatch (inq->vendor, vendor, SID_VENDOR_SIZE)) ||
+ (product && cam_strmatch (inq->product, product, SID_PRODUCT_SIZE)))
+ retval = 1;
+
+ ret:
+ free (cdm.patterns);
+ free (cdm.matches);
+ return (retval);
+ }
+
+ void
+ sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ int fd;
+ struct ccb_dev_match cdm;
+ struct periph_match_pattern *pattern;
+ struct periph_match_result *result;
+ int i;
+ char devname[16];
+
+ DBG_INIT ();
+
+ if ((fd = open (XPT_DEVICE, O_RDWR)) == -1)
+ {
+ DBG (1, "could not open %s\n", XPT_DEVICE);
+ return;
+ }
+
+ /* build ccb for device match */
+ bzero (&cdm, sizeof (cdm));
+ cdm.ccb_h.func_code = XPT_DEV_MATCH;
+
+ /* result buffer */
+ cdm.match_buf_len = sizeof (struct dev_match_result) * 100;
+ cdm.matches = (struct dev_match_result *) malloc (cdm.match_buf_len);
+ cdm.num_matches = 0;
+
+ /* pattern buffer */
+ cdm.num_patterns = 1;
+ cdm.pattern_buf_len = sizeof (struct dev_match_pattern);
+ cdm.patterns = (struct dev_match_pattern *) malloc (cdm.pattern_buf_len);
+
+ /* assemble conditions ... findchannel is ignored */
+ cdm.patterns[0].type = DEV_MATCH_PERIPH;
+ pattern = &cdm.patterns[0].pattern.periph_pattern;
+ pattern->flags = PERIPH_MATCH_NAME;
+ strcpy (pattern->periph_name, "pass");
+ if (findbus != -1)
+ {
+ pattern->path_id = findbus;
+ pattern->flags |= PERIPH_MATCH_PATH;
+ }
+ if (findid != -1)
+ {
+ pattern->target_id = findid;
+ pattern->flags |= PERIPH_MATCH_TARGET;
+ }
+ if (findlun != -1)
+ {
+ pattern->target_lun = findlun;
+ pattern->flags |= PERIPH_MATCH_LUN;
+ }
+
+ /* result loop */
+ do
+ {
+ if (ioctl (fd, CAMIOCOMMAND, &cdm) == -1)
+ {
+ DBG (1, "error sending CAMIOCOMMAND ioctl");
+ break;
+ }
+
+ if ((cdm.ccb_h.status != CAM_REQ_CMP)
+ || ((cdm.status != CAM_DEV_MATCH_LAST)
+ && (cdm.status != CAM_DEV_MATCH_MORE)))
+ {
+ DBG (1, "got CAM error %#x, CDM error %d\n",
+ cdm.ccb_h.status, cdm.status);
+ break;
+ }
+
+ for (i = 0; i < cdm.num_matches; i++)
+ {
+ if (cdm.matches[i].type != DEV_MATCH_PERIPH)
+ continue;
+ result = &cdm.matches[i].result.periph_result;
+ DBG (4, "%s%d on scbus%d %d:%d\n",
+ result->periph_name, result->unit_number,
+ result->path_id, result->target_id, result->target_lun);
+ if (cam_compare_inquiry (fd, result->path_id,
+ result->target_id, result->target_lun,
+ findvendor, findmodel, findtype) == 0)
+ {
+ sprintf (devname, "/dev/%s%d", result->periph_name,
+ result->unit_number);
+ (*attach) (devname);
+ }
+ }
+ }
+ while ((cdm.ccb_h.status == CAM_REQ_CMP)
+ && (cdm.status == CAM_DEV_MATCH_MORE));
+
+ free (cdm.patterns);
+ free (cdm.matches);
+ close (fd);
+ return;
+ }
+
+#endif
+
+
+
+#if USE == HPUX_INTERFACE
+/* XXX untested code! */
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ struct sctl_io hdr;
+ /* xxx obsolete size_t cdb_size;
+
+ cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ memset (&hdr, 0, sizeof (hdr));
+ memcpy (hdr.cdb, cmd, cmd_size);
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete assert (cdb_size == src_size);
+ */
+ hdr.flags = SCTL_READ;
+ hdr.data = dst;
+ hdr.data_length = *dst_size;
+ }
+ else
+ {
+ /* xxx obsolete assert (cdb_size <= src_size);
+ */
+ hdr.data = (char *) src;
+ hdr.data_length = src_size;
+ }
+ hdr.cdb_length = cmd_size;
+ hdr.max_msecs = sane_scsicmd_timeout * 1000;
+ if (ioctl (fd, SIOC_IO, &hdr) < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl(SIOC_IO) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (hdr.cdb_status)
+ DBG (1, "sanei_scsi_cmd: SCSI completed with cdb_status=%d\n",
+ hdr.cdb_status);
+ if (dst_size)
+ *dst_size = hdr.data_xfer;
+
+ if (hdr.sense_xfer > 0 && (hdr.sense[0] & 0x80)
+ && fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, hdr.sense,
+ fd_info[fd].sense_handler_arg);
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == HPUX_INTERFACE */
+
+
+#if USE == OPENSTEP_INTERFACE
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ struct scsi_req hdr;
+ /* xxx obsolete size_t cdb_size;
+
+ cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ memset (&hdr, 0, sizeof (hdr));
+ memcpy (&hdr.sr_cdb, cmd, cmd_size);
+ hdr.sr_cdb_length = cmd_size;
+
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete assert (cdb_size == src_size);
+ */
+ hdr.sr_dma_dir = SR_DMA_RD;
+ hdr.sr_addr = dst;
+ hdr.sr_dma_max = *dst_size;
+ }
+ else
+ {
+ /* xxx obsolete assert (cdb_size <= src_size);
+ */
+ hdr.sr_dma_dir = SR_DMA_WR;
+ hdr.sr_addr = (char *) src;
+ hdr.sr_dma_max = src_size;
+ }
+ hdr.sr_ioto = sane_scsicmd_timeout;
+
+ if (ioctl (fd, SGIOCREQ, &hdr) == -1)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl(SGIOCREQ) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (hdr.sr_io_status != 1)
+ DBG (1, "sanei_scsi_cmd: SGIOCREQ completed with sr_io_status=%d\n",
+ hdr.sr_io_status);
+
+ if (hdr.sr_io_status == SR_IOST_CHKSNV)
+ {
+ struct scsi_req sr;
+ struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
+ struct esense_reply sense_reply;
+ int i;
+ char *p;
+
+ /* clear struct */
+ p = (char *) cdbp;
+ for (i = 0; i < sizeof (union cdb); i++)
+ *p++ = 0;
+ memset (&sr, 0, sizeof (struct scsi_req));
+
+ cdbp->c6_opcode = C6OP_REQSENSE;
+ cdbp->c6_lun = 0; /* where do I get the lun from? */
+ cdbp->c6_len = 0x20;
+ cdbp->c6_ctrl = 0;
+
+ sr.sr_dma_dir = SR_DMA_RD;
+ sr.sr_addr = (char *) &sense_reply;
+ sr.sr_dma_max = sizeof (struct esense_reply);
+ sr.sr_ioto = sane_scsicmd_timeout;
+ sr.sr_cdb_length = 6;
+
+ ioctl (fd, SGIOCREQ, &sr);
+ if (sense_reply.er_ibvalid)
+ {
+ sr.sr_esense = sense_reply;
+ if (fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler)
+ (fd, (u_char *) & sr.sr_esense,
+ fd_info[fd].sense_handler_arg);
+ }
+
+ /* sense reply is invalid */
+ return SANE_STATUS_INVAL;
+ }
+
+ if (hdr.sr_scsi_status == SR_IOST_CHKSV && fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, (u_char *) & hdr.sr_esense,
+ fd_info[fd].sense_handler_arg);
+ if (dst_size)
+ *dst_size = hdr.sr_dma_xfr;
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == OPENSTEP_INTERFACE */
+
+
+#if USE == DECUNIX_INTERFACE
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ u_char sense[64];
+ UAGT_CAM_CCB hdr;
+ CCB_SCSIIO ccb;
+ /* xxx obsolete size_t cdb_size;
+
+ cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ memset (&ccb, 0, sizeof (ccb));
+ ccb.cam_ch.my_addr = (CCB_HEADER *) & ccb;
+ ccb.cam_ch.cam_ccb_len = sizeof (ccb);
+ ccb.cam_ch.cam_func_code = XPT_SCSI_IO;
+ ccb.cam_ch.cam_path_id = fd_info[fd].bus;
+ ccb.cam_ch.cam_target_id = fd_info[fd].target;
+ ccb.cam_ch.cam_target_lun = fd_info[fd].lun;
+ ccb.cam_ch.cam_flags = 0;
+
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete assert (cdb_size == src_size);
+ */
+ ccb.cam_ch.cam_flags |= CAM_DIR_IN;
+ ccb.cam_data_ptr = (u_char *) dst;
+ ccb.cam_dxfer_len = *dst_size;
+ }
+ else
+ {
+ /* xxx obsolete assert (cdb_size <= src_size);
+ */
+ if (0 == src_size)
+ ccb.cam_ch.cam_flags |= CAM_DIR_NONE;
+ else
+ ccb.cam_ch.cam_flags |= CAM_DIR_OUT;
+ ccb.cam_data_ptr = (u_char *) src;
+ ccb.cam_dxfer_len = src_size;
+ }
+ ccb.cam_timeout = sane_scsicmd_timeout;
+ ccb.cam_cdb_len = cmd_size;
+ memcpy (&ccb.cam_cdb_io.cam_cdb_bytes[0], cmd, cmd_size);
+
+ memset (&hdr, 0, sizeof (hdr));
+ hdr.uagt_ccb = (CCB_HEADER *) & ccb;
+ hdr.uagt_ccblen = sizeof (ccb);
+ hdr.uagt_buffer = ccb.cam_data_ptr;
+ hdr.uagt_buflen = ccb.cam_dxfer_len;
+ hdr.uagt_snsbuf = sense;
+ hdr.uagt_snslen = sizeof (sense);
+ hdr.uagt_cdb = 0; /* indicate that CDB is in CCB */
+ hdr.uagt_cdblen = 0;
+
+ if (ioctl (cam_fd, UAGT_CAM_IO, &hdr) < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl(UAGT_CAM_IO) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (ccb.cam_ch.cam_status != CAM_REQ_CMP)
+ {
+ DBG (1, "sanei_scsi_cmd: UAGT_CAM_IO completed with cam_status=%d\n",
+ ccb.cam_ch.cam_status);
+
+ if (ccb.cam_ch.cam_status == CAM_AUTOSNS_VALID
+ && fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, sense,
+ fd_info[fd].sense_handler_arg);
+ else
+ return SANE_STATUS_INVAL;
+ }
+ if (dst_size)
+ *dst_size = ccb.cam_dxfer_len;
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == DECUNIX_INTERFACE */
+
+
+#if USE == SCO_OS5_INTERFACE
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ static u_char sense_buffer[256];
+ struct scsicmd2 sc2;
+ struct scsicmd *sc;
+ /* xxx obsolete int cdb_size;
+ */
+ int opcode;
+ int i;
+
+ if (fd < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ memset (&sc2, 0, sizeof (sc2));
+ sc = &sc2.cmd;
+ sc2.sense_len = sizeof (sense_buffer);
+ sc2.sense_ptr = sense_buffer;
+
+ /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+ if (dst_size && *dst_size)
+ {
+ sc->is_write = 0;
+ sc->data_ptr = dst;
+ sc->data_len = *dst_size;
+ }
+ else
+ {
+ sc->data_len = src_size;
+ sc->data_ptr = (char *) src;
+ sc->is_write = 1;
+ }
+ memcpy (sc->cdb, cmd, cmd_size);
+ sc->cdb_len = cmd_size;
+
+ /* Send the command down via the "pass-through" interface */
+ if (ioctl (fd, SCSIUSERCMD2, &sc2) < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl(SCSIUSERCMD2) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (sc->host_sts || sc->target_sts)
+ {
+ DBG (1, "sanei_scsi_cmd: SCSIUSERCMD2 completed with "
+ "host_sts=%x, target_sts=%x\n", sc->host_sts, sc->target_sts);
+ if (fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, sense_buffer,
+ fd_info[fd].sense_handler_arg);
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == SCO_OS5_INTERFACE */
+#if USE == SYSVR4_INTERFACE
+
+/*
+ * UNIXWARE 2.x interface
+ * (c) R=I+S Rapp Informatik System Germany
+ * Email: wolfgang@rapp-informatik.de
+ *
+ * The driver version should run with other scsi componets like disk
+ * attached to the same controller at the same time.
+ *
+ * Attention : This port needs a sane kernel driver for Unixware 2.x
+ * The driver is available in binary pkgadd format
+ * Plese mail me.
+ *
+ */
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ struct sb sb, *sb_ptr; /* Command block and pointer */
+ struct scs *scs; /* group 6 command pointer */
+ struct scm *scm; /* group 10 command pointer */
+ struct scv *scv; /* group 12 command pointer */
+ char sense[32]; /* for call of sens req */
+ char cmd[16]; /* global for right alignment */
+ char *cp;
+
+ /* xxx obsolete size_t cdb_size;
+
+ cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+ memset (&cmd, 0, 16);
+ sb_ptr = &sb;
+ sb_ptr->sb_type = ISCB_TYPE;
+ cp = (char *) cmd;
+ DBG (1,
+ "cdb_size = %d src = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x ...}\n",
+ cmd_size, cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7],
+ cp[8], cp[9]);
+ switch (cmd_size)
+ {
+ default:
+ return SANE_STATUS_IO_ERROR;
+ case 6:
+ scs = (struct scs *) cmd;
+ memcpy (SCS_AD (scs), cmd, SCS_SZ);
+ scs->ss_lun = 0;
+ sb_ptr->SCB.sc_cmdpt = SCS_AD (scs);
+ sb_ptr->SCB.sc_cmdsz = SCS_SZ;
+ break;
+ case 10:
+ scm = (struct scm *) cmd;
+ memcpy (SCM_AD (scm), cmd, SCM_SZ);
+ scm->sm_lun = 0;
+ sb_ptr->SCB.sc_cmdpt = SCM_AD (scm);
+ sb_ptr->SCB.sc_cmdsz = SCM_SZ;
+ break;
+ case 12:
+ scv = (struct scv *) cmd;
+ memcpy (SCV_AD (scv), cmd, SCV_SZ);
+ scv->sv_lun = 0;
+ sb_ptr->SCB.sc_cmdpt = SCV_AD (scv);
+ sb_ptr->SCB.sc_cmdsz = SCV_SZ;
+ break;
+ }
+ if (dst_size && *dst_size)
+ {
+ assert (0 == src_size);
+ sb_ptr->SCB.sc_mode = SCB_READ;
+ sb_ptr->SCB.sc_datapt = dst;
+ sb_ptr->SCB.sc_datasz = *dst_size;
+ }
+ else
+ {
+ assert (0 <= src_size);
+ sb_ptr->SCB.sc_mode = SCB_WRITE;
+ sb_ptr->SCB.sc_datapt = (char *) src;
+ if ((sb_ptr->SCB.sc_datasz = src_size) > 0)
+ {
+ sb_ptr->SCB.sc_mode = SCB_WRITE;
+ }
+ else
+ {
+ /* also use READ mode if the backends have write with length 0 */
+ sb_ptr->SCB.sc_mode = SCB_READ;
+ }
+ }
+ sb_ptr->SCB.sc_time = sane_scsicmd_timeout * 1000;
+ DBG (1, "sanei_scsi_cmd: sc_mode = %d, sc_cmdsz = %d, sc_datasz = %d\n",
+ sb_ptr->SCB.sc_mode, sb_ptr->SCB.sc_cmdsz, sb_ptr->SCB.sc_datasz);
+ {
+ /* do read write by normal read or write system calls */
+ /* the driver will lock process in momory and do optimized transfer */
+ cp = (char *) cmd;
+ switch (*cp)
+ {
+ case 0x0: /* test unit ready */
+ if (ioctl (fd, SS_TEST, NULL) < 0)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ break;
+ case SS_READ:
+ case SM_READ:
+ if (*dst_size > 0x2048)
+ {
+ sb_ptr->SCB.sc_datapt = NULL;
+ sb_ptr->SCB.sc_datasz = 0;
+ if (memcmp
+ (sb_ptr->SCB.sc_cmdpt, lastrcmd, sb_ptr->SCB.sc_cmdsz))
+ {
+ /* set the command block for the next read or write */
+ memcpy (lastrcmd, sb_ptr->SCB.sc_cmdpt,
+ sb_ptr->SCB.sc_cmdsz);
+ if (!ioctl (fd, SDI_SEND, sb_ptr))
+ {
+ *dst_size = read (fd, dst, *dst_size);
+ if (*dst_size == -1)
+ {
+ perror ("sanei-scsi:UW-driver read ");
+ return SANE_STATUS_IO_ERROR;
+ }
+ break;
+ }
+ }
+ else
+ {
+ *dst_size = read (fd, dst, *dst_size);
+ if (*dst_size == -1)
+ {
+ perror ("sanei-scsi:UW-driver read ");
+ return SANE_STATUS_IO_ERROR;
+ }
+ break;
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* fall through for small read */
+ default:
+ if (ioctl (fd, SDI_SEND, sb_ptr) < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl(SDI_SEND) FAILED: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (dst_size)
+ *dst_size = sb_ptr->SCB.sc_datasz;
+#ifdef UWSUPPORTED /* at this time not supported by driver */
+ if (sb_ptr->SCB.sc_comp_code != SDI_ASW)
+ {
+ DBG (1, "sanei_scsi_cmd: scsi_cmd failture %x\n",
+ sb_ptr->SCB.sc_comp_code);
+ if (sb_ptr->SCB.sc_comp_code == SDI_CKSTAT
+ && sb_ptr->SCB.sc_status == S_CKCON)
+ if (fd_info[fd].sense_handler)
+ {
+ void *arg = fd_info[fd].sense_handler_arg;
+ return (*fd_info[fd].sense_handler) (fd,
+ (u_char *) & sb_ptr->
+ SCB.sc_link, arg);
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+#endif
+ break;
+ }
+ return SANE_STATUS_GOOD;
+ }
+ }
+#endif /* USE == SYSVR4_INTERFACE */
+#if USE == SCO_UW71_INTERFACE
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ static u_char sense_buffer[24];
+ struct scb cmdblk;
+ time_t elapsed;
+ uint_t compcode, status;
+ /* xxx obsolete int cdb_size, mode;
+ */
+ int mode;
+ int i;
+
+ if (fd < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ cmdblk.sc_cmdpt = (caddr_t) cmd;
+ /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+ cmdblk.sc_cmdsz = cmd_size;
+ cmdblk.sc_time = 60000; /* 60 secs */
+
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete assert (cdb_size == src_size);
+ */
+ cmdblk.sc_datapt = (caddr_t) dst;
+ cmdblk.sc_datasz = *dst_size;
+ mode = SCB_READ;
+ }
+ else
+ {
+ /* xxx obsolete assert (cdb_size <= src_size);
+ */
+ cmdblk.sc_datapt = (char *) src;
+ cmdblk.sc_datasz = src_size;
+ mode = SCB_WRITE;
+ }
+
+ if (pt_send (fd, cmdblk.sc_cmdpt, cmdblk.sc_cmdsz, cmdblk.sc_datapt,
+ cmdblk.sc_datasz, mode, cmdblk.sc_time, &elapsed, &compcode,
+ &status, sense_buffer, sizeof (sense_buffer)) != 0)
+ {
+ DBG (1, "sanei_scsi_cmd: pt_send failed: %s!\n", strerror (errno));
+ }
+ else
+ {
+ DBG (2, "sanei_scsi_cmd completed with: compcode = %x, status = %x\n",
+ compcode, status);
+
+ switch (compcode)
+ {
+ case SDI_ASW: /* All seems well */
+ return SANE_STATUS_GOOD;
+ case SDI_CKSTAT:
+ DBG (2, "Sense Data:\n");
+ for (i = 0; i < sizeof (sense_buffer); i++)
+ DBG (2, "%.2X ", sense_buffer[i]);
+ DBG (2, "\n");
+ if (fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, sense_buffer,
+ fd_info[fd].
+ sense_handler_arg);
+ /* fall through */
+ default:
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ }
+#endif /* USE == SCO_UW71_INTERFACE */
+
+#if USE == OS2_INTERFACE
+
+#define WE_HAVE_FIND_DEVICES
+
+ static int
+ get_devicename (int bus, int target, int lun, char *name, size_t name_len)
+ {
+ snprintf (name, name_len, "b%dt%dl%d", bus, target, lun);
+ DBG (1, "OS/2 searched device is %s\n", name);
+ return 0;
+ }
+
+ void
+ sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ size_t findvendor_len = 0, findmodel_len = 0, findtype_len = 0;
+ char vendor[32], model[32], type[32], revision[32];
+ int bus, channel, id, lun, number, i;
+ char line[256], dev_name[128];
+ const char *string;
+ FILE *proc_fp;
+ char *end;
+ struct
+ {
+ const char *name;
+ size_t name_len;
+ int is_int; /* integer valued? (not a string) */
+ union
+ {
+ void *v; /* avoids compiler warnings... */
+ char *str;
+ int *i;
+ }
+ u;
+ }
+ param[] =
+ {
+ {
+ "Vendor:", 7, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "Model:", 6, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "Type:", 5, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "Rev:", 4, 0,
+ {
+ 0}
+ }
+ ,
+ {
+ "scsi", 4, 1,
+ {
+ 0}
+ }
+ ,
+ {
+ "Channel:", 8, 1,
+ {
+ 0}
+ }
+ ,
+ {
+ "Id:", 3, 1,
+ {
+ 0}
+ }
+ ,
+ {
+ "Lun:", 4, 1,
+ {
+ 0}
+ }
+ };
+
+ param[0].u.str = vendor;
+ param[1].u.str = model;
+ param[2].u.str = type;
+ param[3].u.str = revision;
+ param[4].u.i = &bus;
+ param[5].u.i = &channel;
+ param[6].u.i = &id;
+ param[7].u.i = &lun;
+
+ DBG_INIT ();
+
+ open_aspi (); /* open aspi manager if not already done */
+
+ DBG (2, "find_devices: open temporary file '%s'\n", tmpAspi);
+ proc_fp = fopen (tmpAspi, "r");
+ if (!proc_fp)
+ {
+ DBG (1, "could not open %s for reading\n", tmpAspi);
+ return;
+ }
+
+ number = bus = channel = id = lun = -1;
+
+ vendor[0] = model[0] = type[0] = '\0';
+ if (findvendor)
+ findvendor_len = strlen (findvendor);
+ if (findmodel)
+ findmodel_len = strlen (findmodel);
+ if (findtype)
+ findtype_len = strlen (findtype);
+
+ while (!feof (proc_fp))
+ {
+ if (!fgets (line, sizeof (line), proc_fp))
+ break; /* at eof exit */
+
+ string = sanei_config_skip_whitespace (line);
+
+ while (*string)
+ {
+ for (i = 0; i < NELEMS (param); ++i)
+ {
+ if (strncmp (string, param[i].name, param[i].name_len) == 0)
+ {
+ string += param[i].name_len;
+ string = sanei_config_skip_whitespace (string);
+ if (param[i].is_int)
+ {
+ *param[i].u.i = strtol (string, &end, 10);
+ string = (char *) end;
+ }
+ else
+ {
+ strncpy (param[i].u.str, string, 32);
+ param[i].u.str[31] = '\0';
+ while (*string && !isspace ((int) *string))
+ ++string;
+ }
+ string = sanei_config_skip_whitespace (string);
+
+ if (param[i].u.v == &bus)
+ ++number;
+ break;
+ }
+ }
+ if (i >= NELEMS (param))
+ ++string; /* no match */
+ }
+
+ if ((findvendor && !vendor[0]) || (findmodel && !model[0])
+ || (findtype && !type[0])
+ || (findbus >= 0 && bus == -1) || (findchannel >= 0
+ && channel == -1)
+ || (findlun >= 0 && lun == -1))
+ /* some info is still missing */
+ continue;
+
+ if ((!findvendor || strncmp (vendor, findvendor, findvendor_len) == 0)
+ && (!findmodel || strncmp (model, findmodel, findmodel_len) == 0)
+ && (!findtype || strncmp (type, findtype, findtype_len) == 0)
+ && (findbus == -1 || bus == findbus)
+ && (findchannel == -1 || channel == findchannel)
+ && (findid == -1 || id == findid)
+ && (findlun == -1 || lun == findlun)
+ && get_devicename (bus, id, lun, dev_name, sizeof (dev_name)) >= 0
+ && (*attach) (dev_name) != SANE_STATUS_GOOD)
+ return;
+
+ vendor[0] = model[0] = type[0] = 0;
+ bus = channel = id = lun = -1;
+ }
+
+ DBG (2, "find_devices: close temporary file '%s'\n", tmpAspi);
+ fclose (proc_fp);
+
+ close_aspi (); /* close aspi manager */
+ }
+
+/* XXX untested code! */
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ ULONG rc; /* Returns. */
+ unsigned long cbreturn;
+ unsigned long cbParam;
+ if (aspi_buf == NULL) /* avoid SIGSEGV in memcpy() when calling
+ sanei_scsi_cmd2() while aspi-driver is closed */
+ {
+ DBG (1, "sanei_scsi_cmd: Error no device (aspi_buf == NULL)\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (PSRBlock == NULL) /* avoid SIGSEGV in memcpy() when calling
+ sanei_scsi_cmd2() while aspi-driver is closed */
+ {
+ DBG (1, "sanei_scsi_cmd: Error no device (PSRBlock == NULL)\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ memset (PSRBlock, 0, sizeof (SRB)); /* Okay, I'm paranoid. */
+ PSRBlock->cmd = SRB_Command; /* execute SCSI cmd */
+ PSRBlock->ha_num = fd_info[fd].bus; /* host adapter number */
+ PSRBlock->u.cmd.target = fd_info[fd].target; /* Target SCSI ID */
+ PSRBlock->u.cmd.lun = fd_info[fd].lun; /* Target SCSI LUN */
+ PSRBlock->flags = SRB_Post; /* posting enabled */
+ if (dst_size && *dst_size)
+ {
+ /* Reading. */
+ assert (*dst_size <= (size_t) sanei_scsi_max_request_size);
+ PSRBlock->u.cmd.data_len = *dst_size;
+ DBG (1, "sanei_scsi_cmd: Reading PSRBlock->u.cmd.data_len= %lu\n",
+ PSRBlock->u.cmd.data_len);
+ PSRBlock->flags |= SRB_Read;
+ }
+ else
+ {
+ /* Writing. */
+ PSRBlock->u.cmd.data_len = src_size;
+ DBG (1, "sanei_scsi_cmd: Writing PSRBlock->u.cmd.data_len= %lu\n",
+ PSRBlock->u.cmd.data_len);
+ assert (PSRBlock->u.cmd.data_len <=
+ (unsigned long) sanei_scsi_max_request_size);
+ if (PSRBlock->u.cmd.data_len)
+ PSRBlock->flags |= SRB_Write;
+ else
+ PSRBlock->flags |= SRB_NoTransfer;
+ memcpy (aspi_buf, src, PSRBlock->u.cmd.data_len);
+ }
+ PSRBlock->u.cmd.sense_len = 32; /* length of sense buffer */
+ PSRBlock->u.cmd.data_ptr = NULL; /* pointer to data buffer already registered */
+ PSRBlock->u.cmd.link_ptr = NULL; /* pointer to next SRB */
+ PSRBlock->u.cmd.cdb_len = cmd_size; /* SCSI command length */
+ memcpy (PSRBlock->u.cmd.cdb_st, cmd, cmd_size);
+
+ /* Do the command. */
+ rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
+ (void *) PSRBlock, sizeof (SRB), &cbParam,
+ (void *) PSRBlock, sizeof (SRB), &cbreturn);
+
+ if (rc)
+ {
+ DBG (1, "sanei_scsi_cmd: DosDevIOCtl failed. rc= %lu \n", rc);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Get sense data if available. */
+ if ((PSRBlock->status == SRB_Aborted || PSRBlock->status == SRB_Error) &&
+ PSRBlock->u.cmd.target_status == SRB_CheckStatus
+ && fd_info[fd].sense_handler != 0)
+ {
+ SANEI_SCSI_Sense_Handler s_handler = fd_info[fd].sense_handler;
+ return (*s_handler) (fd, &PSRBlock->u.cmd.cdb_st[cmd_size],
+ fd_info[fd].sense_handler_arg);
+ }
+ if (PSRBlock->status != SRB_Done ||
+ PSRBlock->u.cmd.ha_status != SRB_NoError ||
+ PSRBlock->u.cmd.target_status != SRB_NoStatus)
+ {
+ DBG (1, "sanei_scsi_cmd: command 0x%02x failed.\n"
+ "PSRBlock->status= 0x%02x\n"
+ "PSRBlock->u.chm.ha_status= 0x%02x\n"
+ "PSRBlock->u.cmd.target_status= 0x%02x\n",
+ PSRBlock->u.cmd.cdb_st[0],
+ PSRBlock->status,
+ PSRBlock->u.cmd.ha_status, PSRBlock->u.cmd.target_status);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (dst_size && *dst_size) /* Reading? */
+ memcpy ((char *) dst, aspi_buf, *dst_size);
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == OS2_INTERFACE */
+
+#if USE == STUBBED_INTERFACE
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* USE == STUBBED_INTERFACE */
+
+#if USE == IRIX_INTERFACE
+
+#define WE_HAVE_FIND_DEVICES
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ dsreq_t scsi_req; /* SCSI request */
+/* xxx obsolete size_t cdb_size; *//* Size of SCSI command */
+ static u_char *cmdbuf = NULL, /* Command buffer */
+ *sensebuf = NULL, /* Request sense buffer */
+ *databuf = NULL; /* Data buffer */
+
+ /*
+ * Allocate the sense and command data buffers as necessary; we have
+ * to do this to avoid buffer alignment problems, since some
+ * hardware requires these buffers to be 32-bit aligned.
+ */
+ if (cmdbuf == NULL)
+ {
+ cmdbuf = malloc (64);
+ sensebuf = malloc (1024); /* may be can reduced to 128 */
+ databuf = malloc (MAX_DATA);
+
+ if (cmdbuf == NULL || sensebuf == NULL || databuf == NULL)
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /*
+ * Build the SCSI request...
+ */
+ /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ DBG (1, "sanei_scsi_cmd: cmd_size = %d\n", cmd_size);
+
+ if (dst != NULL)
+ {
+ /*
+ * SCSI command returning/reading data...
+ */
+ scsi_req.ds_flags = DSRQ_READ | DSRQ_SENSE;
+ scsi_req.ds_time = 120 * 1000;
+ scsi_req.ds_cmdbuf = (caddr_t) cmdbuf;
+ scsi_req.ds_cmdlen = cmd_size;
+ scsi_req.ds_databuf = (caddr_t) databuf;
+ scsi_req.ds_datalen = *dst_size;
+ scsi_req.ds_sensebuf = (caddr_t) sensebuf;
+ scsi_req.ds_senselen = 128; /* 1024 does not work, 128 is tested (O.Rauch) */
+
+ /*
+ * Copy command to cmdbuf to assure 32-bit alignment.
+ */
+ memcpy (cmdbuf, cmd, cmd_size);
+ }
+ else
+ {
+ /*
+ * SCSI command sending/writing data...
+ */
+ scsi_req.ds_flags = DSRQ_WRITE | DSRQ_SENSE;
+ scsi_req.ds_time = 120 * 1000;
+ scsi_req.ds_cmdbuf = (caddr_t) cmdbuf;
+ scsi_req.ds_cmdlen = cmd_size;
+ scsi_req.ds_databuf = (caddr_t) databuf;
+ scsi_req.ds_datalen = src_size;
+ scsi_req.ds_sensebuf = (caddr_t) sensebuf;
+ scsi_req.ds_senselen = 128;
+
+ /*
+ * Copy command and data to local buffers to ensure 32-bit alignment...
+ */
+ memcpy (cmdbuf, (u_char *) cmd, cmd_size);
+ memcpy (databuf, (u_char *) src, src_size);
+ }
+
+ bzero (sensebuf, 128);
+
+ /*
+ * Do SCSI request...
+ */
+ if (ioctl (fd, DS_ENTER, &scsi_req) < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl failed - %s\n", strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (1, "sanei_scsi_cmd: status = %d\n", scsi_req.ds_status);
+
+ /*
+ * Set the incoming data size and copy the destination data as needed...
+ */
+ if (dst != NULL)
+ {
+ *dst_size = scsi_req.ds_datasent;
+
+ DBG (1, "sanei_scsi_cmd: read %d bytes\n", scsi_req.ds_datasent);
+
+ if (scsi_req.ds_datasent > 0)
+ memcpy (dst, databuf, scsi_req.ds_datasent);
+ }
+
+ /*
+ * Return the appropriate status code...
+ */
+ if (scsi_req.ds_status != 0)
+ {
+ if (scsi_req.ds_status == STA_BUSY)
+ return SANE_STATUS_DEVICE_BUSY;
+ else if (fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, sensebuf,
+ fd_info[fd].sense_handler_arg);
+ else
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+ }
+
+ void
+ sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ size_t findvendor_len = 0, findmodel_len = 0;
+ /* Lengths of search strings */
+ inventory_t *inv; /* Current hardware inventory entry */
+ int bus, id, lun; /* Current Bus, ID, and LUN */
+ char dev_name[128]; /* SCSI device name */
+ int fd; /* SCSI file */
+ size_t inqsize; /* Size of returned inquiry data */
+ char vendor[9], /* Vendor name */
+ model[17]; /* Model/product name */
+ u_char inqdata[128], /* Inquiry data buffer */
+ inqcommand[6]; /* Inquiry command (0x12) buffer */
+
+ DBG_INIT ();
+
+ vendor[0] = model[0] = '\0';
+ if (findvendor)
+ findvendor_len = strlen (findvendor);
+ if (findmodel)
+ findmodel_len = strlen (findmodel);
+
+ if (findvendor != NULL)
+ DBG (1, "sanei_scsi_find_devices: looking for vendors starting "
+ "with \"%s\".\n", findvendor);
+
+ if (findmodel != NULL)
+ DBG (1, "sanei_scsi_find_devices: looking for models starting "
+ "with \"%s\".\n", findmodel);
+
+ setinvent ();
+
+ while ((inv = getinvent ()) != NULL)
+ {
+ if (inv->inv_class != INV_SCSI ||
+ (inv->inv_type != INV_SCANNER && inv->inv_type != INV_CPU))
+ continue;
+
+ bus = inv->inv_controller;
+ id = inv->inv_unit;
+ lun = inv->inv_state >> 8;
+
+ DBG (1, "sanei_scsi_find_devices: found %s on controller %d, "
+ "ID %d, LUN %d.\n",
+ inv->inv_type == INV_SCANNER ? "scanner" : "processor",
+ bus, id, lun);
+
+ if ((findbus >= 0 && bus != findbus) ||
+ (findid >= 0 && id != findid) || (findlun >= 0 && lun != findlun))
+ {
+ DBG (1, "sanei_scsi_find_devices: ignoring this device.\n");
+ continue;
+ }
+
+ sprintf (dev_name, "/dev/scsi/sc%dd%dl%d", bus, id, lun);
+ DBG (1, "sanei_scsi_find_devices: device name is \"%s\".\n",
+ dev_name);
+
+ /*
+ * Open the SCSI device and get the inquiry data...
+ */
+
+ if (sanei_scsi_open (dev_name, &fd, NULL, NULL) != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_scsi_find_devices: unable to open device file - %s.\n",
+ strerror (errno));
+ continue;
+ }
+
+ DBG (1, "sanei_scsi_find_devices: device fd = %d.\n", fd);
+
+ inqsize = sizeof (inqdata);
+
+ inqcommand[0] = 0x12;
+ inqcommand[1] = 0;
+ inqcommand[2] = 0;
+ inqcommand[3] = sizeof (inqdata) >> 8;
+ inqcommand[4] = sizeof (inqdata);
+ inqcommand[5] = 0;
+
+ if (sanei_scsi_cmd (fd, inqcommand, sizeof (inqcommand), inqdata,
+ &inqsize) != SANE_STATUS_GOOD)
+ {
+ DBG (1,
+ "sanei_scsi_find_devices: unable to get inquiry data - %s.\n",
+ strerror (errno));
+ continue;
+ }
+
+ sanei_scsi_close (fd);
+
+ strncpy (vendor, (char *) inqdata + 8, 8);
+ vendor[8] = '\0';
+ strncpy (model, (char *) inqdata + 16, 16);
+ model[16] = '\0';
+
+ DBG (1, "sanei_scsi_find_devices: vendor = \'%s\', model = \'%s'.\n",
+ vendor, model);
+
+ /*
+ * Compare as necessary...
+ */
+
+ if ((findvendor != NULL
+ && strncmp (findvendor, vendor, findvendor_len))
+ || (findmodel != NULL
+ && strncmp (findmodel, model, findmodel_len)))
+ {
+ DBG (1, "sanei_scsi_find_devices: ignoring this device.\n");
+ continue;
+ }
+
+ /*
+ * OK, this one matches, so use it!
+ */
+
+ DBG (1, "sanei_scsi_find_devices: attaching this device.\n");
+
+ (*attach) (dev_name);
+ }
+ }
+#endif /* USE == IRIX_INTERFACE */
+
+#if USE == AIX_GSC_INTERFACE
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ scmd_t scmd;
+ /* xxx obsolete size_t cdb_size;
+ */
+ char sense_buf[32];
+ char status;
+
+ /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ memset (&scmd, 0, sizeof (scmd));
+ if (dst_size && *dst_size)
+ {
+ /* xxx obsolete assert (cdb_size == src_size);
+ */
+ scmd.rw = 1;
+ scmd.data_buf = dst;
+ scmd.datalen = *dst_size;
+ }
+ else
+ {
+ /* assert (cdb_size <= src_size);
+ */
+ scmd.data_buf = (char *) src;
+ scmd.datalen = src_size;
+ }
+ scmd.cdb = (char *) cmd;
+ scmd.cdblen = cmd_size;
+ scmd.timeval = sane_scsicmd_timeout;
+ scmd.sense_buf = sense_buf;
+ scmd.senselen = sizeof (sense_buf);
+ scmd.statusp = &status;
+ DBG (1, "sanei_scsi_cmd: scmd.rw = %d, scmd.cdblen = %d, ",
+ scmd.rw, scmd.cdblen);
+ DBG (1, "scmd.cdb = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, ...}\n",
+ scmd.cdb[0], scmd.cdb[1], scmd.cdb[2],
+ scmd.cdb[3], scmd.cdb[4], scmd.cdb[5]);
+ if (ioctl (fd, GSC_CMD, &scmd) < 0)
+ {
+ DBG (1, "sanei_scsi_cmd: ioctl(SIOC_IO) failed: %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (*scmd.statusp)
+ DBG (1, "sanei_scsi_cmd: SCSI completed with status=%d\n",
+ *scmd.statusp);
+
+ DBG (1, "sanei_scsi_cmd: dst = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, ...}\n",
+ *((char *) dst + 0), *((char *) dst + 1), *((char *) dst + 2),
+ *((char *) dst + 3), *((char *) dst + 4), *((char *) dst + 5));
+
+ if (dst_size)
+ *dst_size = scmd.datalen;
+
+ if (scmd.senselen > 0
+ && (scmd.sense_buf[0] & 0x80) && fd_info[fd].sense_handler)
+ return (*fd_info[fd].sense_handler) (fd, (u_char *) scmd.sense_buf,
+ fd_info[fd].sense_handler_arg);
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == AIX_GSC_INTERFACE */
+
+#if USE == SOLARIS_SG_INTERFACE
+
+#ifndef CCS_SENSE_LEN
+# define CCS_SENSE_LEN 18
+#endif
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ struct user_scsi us;
+ /* xxx obsolete size_t cdb_size;
+ */
+ char sensebf[CCS_SENSE_LEN];
+
+ /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ /* first put the user scsi structure together. */
+ memset (&us, 0, sizeof (us));
+ us.us_cdbp = (caddr_t) cmd;
+ us.us_cdblen = cmd_size;
+ us.us_sensep = sensebf;
+ us.us_senselen = CCS_SENSE_LEN;
+ if (dst && dst_size && *dst_size)
+ {
+ us.us_bufp = (caddr_t) dst;
+ us.us_buflen = *dst_size;
+ us.us_flags = USER_SCSI_READ;
+ }
+ else
+ {
+ us.us_bufp = (caddr_t) src;
+ us.us_buflen = src_size;
+ us.us_flags = USER_SCSI_WRITE;
+ }
+ /* now run it */
+ if (ioctl (fd, USER_SCSI, &us) < 0)
+ return SANE_STATUS_IO_ERROR;
+ if (dst_size)
+ *dst_size -= us.us_resid;
+
+ return SANE_STATUS_GOOD;
+ }
+#endif /* USE == SOLARIS_SG_INTERFACE */
+
+#if USE == SOLARIS_INTERFACE
+
+#ifndef SC_NOT_READ
+# define SC_NOT_READY 0x02
+#endif
+
+#ifndef SC_BUSY
+# define SC_BUSY 0x08
+#endif
+#define DEF_TIMEOUT sane_scsicmd_timeout;
+
+/* Choosing one of the following DEF_SCG_FLG's SCG_DISRE_ENA allows
+ the SCSI driver to disconnect/reconnect. SCG_CMD_RETRY allows a
+ retry if a retryable error occurs.
+
+ Disallowing SCG_DISRE_ENA slows down the operation of the SCSI bus
+ while the scanner is working. If you have severe problems try to
+ set it to 0.
+
+ SCG_CMD_RETRY allows the driver to retry some commands. It should
+ normally be set. For some kinds of odd problems, it may cause the
+ machine to hang for some time. */
+
+#define DEF_SCG_FLG SCG_DISRE_ENA
+/* #define DEF_SCG_FLG 0 */
+/* #define DEF_SCG_FLG SCG_DISRE_ENA | SCG_CMD_RETRY */
+/* #define DEF_SCG_FLG SCG_CMD_RETRY */
+
+ static int d_errs = 100;
+
+ static SANE_Status
+ scsi_cmd (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, int probing)
+ {
+ struct scg_cmd scmd;
+ /* xxx obsolete size_t cdb_size;
+ */
+ SANEI_SCSI_Sense_Handler handler;
+
+ /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src);
+ */
+
+ memset (&scmd, 0, sizeof (scmd));
+ scmd.flags = DEF_SCG_FLG | (probing ? SCG_SILENT : 0);
+ if (dst && dst_size && *dst_size)
+ {
+ /* xxx obsolete assert (cdb_size == src_size);
+ */
+ scmd.flags |= SCG_RECV_DATA;
+ scmd.addr = dst;
+ scmd.size = *dst_size;
+ }
+ else
+ {
+ /* xxx obsolete assert (cdb_size <= src_size);
+ */
+ scmd.addr = (caddr_t) src;
+ scmd.size = src_size;
+ }
+ scmd.cdb_len = cmd_size;
+ scmd.sense_len = CCS_SENSE_LEN;
+ scmd.target = fd_info[fd].target;
+ /* use 2 second timeout when probing, 60 seconds otherwise: */
+ scmd.timeout = probing ? 2 : DEF_TIMEOUT;
+ memcpy (&scmd.cdb.g0_cdb.cmd, cmd, cmd_size);
+ scmd.cdb.cmd_cdb[1] |= fd_info[fd].lun << 5;
+ if (ioctl (fd, SCGIO_CMD, &scmd) < 0)
+ return SANE_STATUS_IO_ERROR;
+ if (dst_size)
+ *dst_size = scmd.size - scmd.resid;
+ if (scmd.error == 0 && scmd.errno == 0 && *(u_char *) & scmd.scb == 0)
+ return SANE_STATUS_GOOD;
+
+ if (scmd.error == SCG_TIMEOUT)
+ DBG (0, "sanei_scsi_cmd %x: timeout\n", scmd.cdb.g0_cdb.cmd);
+ else if (probing)
+ {
+ struct scsi_ext_sense *ext_sense =
+ (struct scsi_ext_sense *) &scmd.sense;
+
+ if (scmd.error < SCG_FATAL
+ && ((scmd.sense.code < 0x70 && scmd.sense.code != 0x04)
+ || (scmd.sense.code >= 0x70
+ && ext_sense->key != SC_NOT_READY)))
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ char errbf[128];
+ int i, rv, lifes;
+
+ handler = fd_info[fd].sense_handler;
+ DBG (3, "cmd=%x, error=%d:%s, bsiz=%d, stat=%x,%x,%x, slen=%d\n",
+ scmd.cdb.g0_cdb.cmd, scmd.error, strerror (scmd.errno),
+ ((dst_size != NULL) ? (*dst_size) : 0), scmd.u_scb.cmd_scb[0],
+ scmd.u_scb.cmd_scb[1], scmd.u_scb.cmd_scb[2], scmd.sense_count);
+ *errbf = '\0';
+ for (i = 0; i < scmd.sense_count; i++)
+ sprintf (errbf + strlen (errbf), "%x,", scmd.u_sense.cmd_sense[i]);
+ DBG (3, "sense=%s\n", errbf);
+
+ /* test_unit_ready on a busy unit returns error = 0 or 2 with
+ errno=EIO. I've seen 0 on a CDrom without a CD, and 2 on a
+ scanner just busy.
+
+ If (SANE_DEBUG_SANEI_SCSI > 100) lifes =
+ SANE_DEBUG_SANEI_SCSI - 100 use up one life for every
+ scmd.error abort and dump core when no lifes left
+ test_unit_ready commands are not counted. */
+ if (scmd.error)
+ {
+ if (sanei_debug_sanei_scsi > 100 &&
+ scmd.cdb.g0_cdb.cmd != SC_TEST_UNIT_READY)
+ {
+ lifes = sanei_debug_sanei_scsi - ++d_errs;
+ DBG (1, "sanei_scsi_cmd: %d lifes left\n", lifes);
+ assert (lifes > 0);
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (scmd.u_scb.cmd_scb[0] == SC_BUSY)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (*(u_char *) & scmd.sense && handler)
+ {
+ rv = (*handler) (fd, scmd.u_sense.cmd_sense,
+ fd_info[fd].sense_handler_arg);
+ DBG (2, "sanei_scsi_cmd: sense-handler returns %d\n", rv);
+ return rv;
+ }
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ return scsi_cmd (fd, cmd, cmd_size, src, src_size, dst, dst_size, 0);
+ }
+
+ static int unit_ready (int fd)
+ {
+ static const u_char test_unit_ready[] = { 0, 0, 0, 0, 0, 0 };
+ int status;
+
+ status = scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0, 0, 0, 1);
+ return (status == SANE_STATUS_GOOD);
+ }
+
+#endif /* USE == SOLARIS_INTERFACE */
+
+
+#if USE == SOLARIS_USCSI_INTERFACE
+
+#define DEF_TIMEOUT sane_scsicmd_timeout;
+
+ static int d_errs = 100;
+ typedef struct scsi_extended_sense extended_sense_t;
+ typedef struct scsi_inquiry scsi_inquiry_t;
+
+ static SANE_Status
+ scsi_cmd (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, int probing)
+ {
+ struct uscsi_cmd us;
+ scsi_inquiry_t inquiry, *iq = &inquiry;
+ extended_sense_t sense, *sp = &sense;
+ SANEI_SCSI_Sense_Handler handler;
+
+ memset (&us, 0, sizeof (us));
+ memset (sp, 0, sizeof (*sp));
+
+ us.uscsi_flags = USCSI_SILENT | USCSI_RQENABLE | USCSI_DIAGNOSE;
+ us.uscsi_timeout = probing ? 2 : DEF_TIMEOUT;
+ us.uscsi_rqbuf = (caddr_t) sp; /* sense data address */
+ us.uscsi_rqlen = sizeof (extended_sense_t); /* length of sense data */
+
+ if (dst && dst_size && *dst_size)
+ {
+ us.uscsi_flags |= USCSI_READ;
+ us.uscsi_bufaddr = (caddr_t) dst;
+ us.uscsi_buflen = *dst_size;
+ }
+ else
+ {
+ us.uscsi_flags |= USCSI_WRITE;
+ us.uscsi_bufaddr = (caddr_t) src;
+ us.uscsi_buflen = src_size;
+ }
+
+ us.uscsi_cdblen = cmd_size;
+ us.uscsi_cdb = (caddr_t) cmd;
+
+ if (ioctl (fd, USCSICMD, &us) < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ if (dst_size)
+ *dst_size = us.uscsi_buflen - us.uscsi_resid;
+
+ if ((us.uscsi_status & STATUS_MASK) == STATUS_GOOD)
+ return SANE_STATUS_GOOD;
+
+ if (sp->es_key == SUN_KEY_TIMEOUT)
+ DBG (0, "sanei_scsi_cmd %x: timeout\n", *(char *) cmd);
+ else
+ {
+ char errbf[128];
+ int i, rv, lifes;
+
+ handler = fd_info[fd].sense_handler;
+ DBG (3, "cmd=%x, scsi_status=%x\n", *(char *) cmd, us.uscsi_status);
+ *errbf = '\0';
+
+ for (i = 0; i < us.uscsi_rqlen; i++)
+ sprintf (errbf + strlen (errbf), "%x,", *(sp + i));
+
+ DBG (3, "sense=%s\n", errbf);
+
+#if 0
+ if (us.error)
+ {
+ if (sanei_debug_sanei_scsi > 100 &&
+ scmd.cdb.g0_cdb.cmd != SC_TEST_UNIT_READY)
+ {
+ lifes = sanei_debug_sanei_scsi - ++d_errs;
+ DBG (1, "sanei_scsi_cmd: %d lifes left\n", lifes);
+ assert (lifes > 0);
+ }
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (scmd.u_scb.cmd_scb[0] == SC_BUSY)
+ return SANE_STATUS_DEVICE_BUSY;
+#endif
+
+ if (handler)
+ {
+ rv = (*handler) (fd, (unsigned char *) sp,
+ fd_info[fd].sense_handler_arg);
+ DBG (2, "sanei_scsi_cmd: sense-handler returns %d\n", rv);
+ return rv;
+ }
+ }
+
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ return scsi_cmd (fd, cmd, cmd_size, src, src_size, dst, dst_size, 0);
+ }
+
+ static int unit_ready (int fd)
+ {
+ static const u_char test_unit_ready[] = { 0, 0, 0, 0, 0, 0 };
+ int status;
+
+ status = scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
+ 0, 0, 0, 0, 1);
+ return (status == SANE_STATUS_GOOD);
+ }
+#endif /* USE == SOLARIS_USCSI_INTERFACE */
+
+#if USE == WIN32_INTERFACE
+
+SANE_Status
+sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+{
+ struct pkt {
+ SCSI_PASS_THROUGH_DIRECT sptd;
+ unsigned char sense[255];
+ } pkt;
+ DWORD BytesReturned;
+ BOOL ret;
+
+ memset(&pkt, 0, sizeof( pkt ));
+ pkt.sptd.Length = sizeof( SCSI_PASS_THROUGH_DIRECT );
+
+ pkt.sptd.PathId = fd_info[fd].bus;
+ pkt.sptd.TargetId = fd_info[fd].target;
+ pkt.sptd.Lun = fd_info[fd].lun;
+
+ assert(cmd_size == 6 || cmd_size == 10 || cmd_size == 12 || cmd_size == 16);
+ memcpy(pkt.sptd.Cdb, cmd, cmd_size);
+ pkt.sptd.CdbLength = cmd_size;
+
+ if (dst_size && *dst_size)
+ {
+ pkt.sptd.DataIn = SCSI_IOCTL_DATA_IN;
+ pkt.sptd.DataTransferLength = *dst_size;
+ pkt.sptd.DataBuffer = dst;
+ }
+ else if (src_size)
+ {
+ pkt.sptd.DataIn = SCSI_IOCTL_DATA_OUT;
+ pkt.sptd.DataTransferLength = src_size;
+ pkt.sptd.DataBuffer = src;
+ }
+ else {
+ pkt.sptd.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+ }
+
+ pkt.sptd.TimeOutValue = sane_scsicmd_timeout;
+
+ pkt.sptd.SenseInfoOffset = (void *)pkt.sense - (void *)&pkt;
+ pkt.sptd.SenseInfoLength = sizeof(pkt.sense);
+
+ ret = DeviceIoControl(fd,
+ IOCTL_SCSI_PASS_THROUGH_DIRECT,
+ &pkt.sptd, sizeof( pkt ),
+ &pkt.sptd, sizeof( pkt ),
+ &BytesReturned, NULL );
+
+ if (ret == 0)
+ {
+ DBG (1, "sanei_scsi_cmd2: DeviceIoControl() failed: %ld\n",
+ GetLastError());
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (pkt.sptd.ScsiStatus == 2){
+ /* Check condition. */
+ SANEI_SCSI_Sense_Handler handler;
+
+ handler = fd_info[fd].sense_handler;
+ if (handler) {
+ return handler(fd, pkt.sense, fd_info[fd].sense_handler_arg);
+ }
+ else {
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else if (pkt.sptd.ScsiStatus != 0) {
+ DBG (1, "sanei_scsi_cmd2: ScsiStatus is %d\n",
+ pkt.sptd.ScsiStatus);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (dst_size) {
+ *dst_size = pkt.sptd.DataTransferLength;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#define WE_HAVE_FIND_DEVICES
+
+/* This is almost the same algorithm used in sane-find-scanner. */
+void
+sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid, int findlun,
+ SANE_Status (*attach) (const char *dev))
+{
+ int hca;
+ HANDLE fd;
+ char scsi_hca_name[20];
+ char buffer[4096];
+ DWORD BytesReturned;
+ BOOL ret;
+ PSCSI_ADAPTER_BUS_INFO adapter;
+ PSCSI_INQUIRY_DATA inquiry;
+ int i;
+
+ DBG_INIT();
+
+ hca = 0;
+
+ for(hca = 0; ; hca++) {
+
+ /* Open the adapter */
+ snprintf(scsi_hca_name, 20, "\\\\.\\Scsi%d:", hca);
+ fd = CreateFile(scsi_hca_name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_RANDOM_ACCESS, NULL );
+
+ if (fd == INVALID_HANDLE_VALUE) {
+ /* Assume there is no more adapter. This is wrong in the case
+ * of hot-plug stuff, but I have yet to see it on a user
+ * machine. */
+ break;
+ }
+
+ /* Get the inquiry info for the devices on that hca. */
+ ret = DeviceIoControl(fd,
+ IOCTL_SCSI_GET_INQUIRY_DATA,
+ NULL,
+ 0,
+ buffer,
+ sizeof(buffer),
+ &BytesReturned,
+ FALSE);
+
+ if(ret == 0)
+ {
+ CloseHandle(fd);
+ continue;
+ }
+
+ adapter = (PSCSI_ADAPTER_BUS_INFO)buffer;
+
+ for(i = 0; i < adapter->NumberOfBuses; i++) {
+
+ if (adapter->BusData[i].InquiryDataOffset == 0) {
+ /* No device here */
+ continue;
+ }
+
+ inquiry = (PSCSI_INQUIRY_DATA) (buffer +
+ adapter->BusData[i].InquiryDataOffset);
+
+ while(1) {
+
+ if ((findvendor == NULL || strncmp(findvendor, (char *)&inquiry->InquiryData[8], 8) == 0)) {
+ DBG(1, "OK1\n");
+ } else {
+ DBG(1, "failed for [%s] and [%s]\n",findvendor, (char *)&inquiry->InquiryData[8] );
+ }
+
+
+ /* Check if this device fits the criteria. */
+ if ((findvendor == NULL || strncmp(findvendor, (char *)&inquiry->InquiryData[8], strlen(findvendor)) == 0) &&
+ (findmodel == NULL || strncmp(findmodel, (char *)&inquiry->InquiryData[16], strlen(findmodel)) == 0) &&
+ (findbus == -1 || findbus == hca) &&
+ (findchannel == -1 || findchannel == inquiry->PathId) &&
+ (findid == -1 || findid == inquiry->TargetId) &&
+ (findlun == -1 || findlun == inquiry->Lun)) {
+
+ char device_name[20];
+ sprintf(device_name, "h%db%dt%dl%d", hca, inquiry->PathId, inquiry->TargetId, inquiry->Lun);
+ attach(device_name);
+ }
+ if (inquiry->NextInquiryDataOffset == 0) {
+ /* No device here */
+ break;
+ } else {
+ inquiry = (PSCSI_INQUIRY_DATA) (buffer +
+ inquiry->NextInquiryDataOffset);
+ }
+ }
+ }
+ CloseHandle(fd);
+
+ }
+}
+#endif /* USE == WIN32_INTERFACE */
+
+#if USE == MACOSX_INTERFACE
+
+# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H
+
+ static SANE_Status
+ sanei_scsi_cmd2_old_api (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ mach_port_t masterPort;
+ IOReturn ioReturnValue;
+ io_object_t scsiDevice;
+ int i;
+ CFMutableDictionaryRef scsiMatchDictionary;
+ int deviceTypeNumber;
+ CFNumberRef deviceTypeRef;
+ io_iterator_t scsiObjectIterator;
+ io_object_t device;
+ CFNumberRef IOUnitRef;
+ int iounit;
+ CFNumberRef scsiTargetRef;
+ int scsitarget;
+ CFNumberRef scsiLunRef;
+ int scsilun;
+ IOCFPlugInInterface **plugInInterface;
+ SInt32 score;
+ HRESULT plugInResult;
+ IOSCSIDeviceInterface **scsiDeviceInterface;
+ IOCDBCommandInterface **cdbCommandInterface;
+ CDBInfo cdb;
+ IOVirtualRange range;
+ UInt32 transferCount;
+ Boolean isWrite;
+ SCSIResults results;
+ UInt32 seqNumber;
+
+ masterPort = 0;
+ ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort);
+ if (ioReturnValue != kIOReturnSuccess || masterPort == 0)
+ {
+ DBG (5, "Could not get I/O master port (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ scsiDevice = 0;
+ for (i = 0; !scsiDevice && i < 2; i++)
+ {
+ scsiMatchDictionary = IOServiceMatching (kIOSCSIDeviceClassName);
+ if (scsiMatchDictionary == NULL)
+ {
+ DBG (5, "Could not create SCSI matching dictionary\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ deviceTypeNumber =
+ (i == 0 ? kSCSIDevTypeScanner : kSCSIDevTypeProcessor);
+ deviceTypeRef = CFNumberCreate (NULL, kCFNumberIntType,
+ &deviceTypeNumber);
+ CFDictionarySetValue (scsiMatchDictionary,
+ CFSTR (kSCSIPropertyDeviceTypeID),
+ deviceTypeRef);
+ CFRelease (deviceTypeRef);
+
+ scsiObjectIterator = 0;
+ ioReturnValue = IOServiceGetMatchingServices (masterPort,
+ scsiMatchDictionary,
+ &scsiObjectIterator);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Could not match services (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ while ((device = IOIteratorNext (scsiObjectIterator)))
+ {
+ IOUnitRef =
+ IORegistryEntryCreateCFProperty (device,
+ CFSTR (kSCSIPropertyIOUnit),
+ NULL, 0);
+ CFNumberGetValue (IOUnitRef, kCFNumberIntType, &iounit);
+ CFRelease (IOUnitRef);
+ scsiTargetRef =
+ IORegistryEntryCreateCFProperty (device,
+ CFSTR (kSCSIPropertyTarget),
+ NULL, 0);
+ CFNumberGetValue (scsiTargetRef, kCFNumberIntType, &scsitarget);
+ CFRelease (scsiTargetRef);
+ scsiLunRef =
+ IORegistryEntryCreateCFProperty (device,
+ CFSTR (kSCSIPropertyLun),
+ NULL, 0);
+ CFNumberGetValue (scsiLunRef, kCFNumberIntType, &scsilun);
+ CFRelease (scsiLunRef);
+
+ if (fd_info[fd].bus == iounit &&
+ fd_info[fd].target == scsitarget &&
+ fd_info[fd].lun == scsilun)
+ scsiDevice = device;
+ else
+ IOObjectRelease (device);
+ }
+ IOObjectRelease (scsiObjectIterator);
+ }
+ if (!scsiDevice)
+ {
+ DBG (5, "Device not found (unit %i, target %i, lun %i)\n",
+ fd_info[fd].bus, fd_info[fd].target, fd_info[fd].lun);
+ return SANE_STATUS_INVAL;
+ }
+
+ plugInInterface = NULL;
+ score = 0;
+ ioReturnValue = IOCreatePlugInInterfaceForService (scsiDevice,
+ kIOSCSIUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface,
+ &score);
+ if (ioReturnValue != kIOReturnSuccess || plugInInterface == NULL)
+ {
+ DBG (5, "Error creating plugin interface (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ scsiDeviceInterface = NULL;
+ plugInResult = (*plugInInterface)->
+ QueryInterface (plugInInterface,
+ CFUUIDGetUUIDBytes (kIOSCSIDeviceInterfaceID),
+ (LPVOID) & scsiDeviceInterface);
+ if (plugInResult != S_OK || scsiDeviceInterface == NULL)
+ {
+ DBG (5, "Couldn't create SCSI device interface (%ld)\n", plugInResult);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ (*plugInInterface)->Release (plugInInterface);
+ IOObjectRelease (scsiDevice);
+
+ ioReturnValue = (*scsiDeviceInterface)->open (scsiDeviceInterface);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error opening SCSI interface (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ cdbCommandInterface = NULL;
+ plugInResult = (*scsiDeviceInterface)->
+ QueryInterface (scsiDeviceInterface,
+ CFUUIDGetUUIDBytes (kIOCDBCommandInterfaceID),
+ (LPVOID) & cdbCommandInterface);
+ if (plugInResult != S_OK || cdbCommandInterface == NULL)
+ {
+ DBG (5, "Error creating CDB interface (%ld)\n", plugInResult);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ cdb.cdbLength = cmd_size;
+ memcpy (&cdb.cdb, cmd, cmd_size);
+ if (dst && dst_size)
+ {
+ bzero (dst, *dst_size);
+ range.address = (IOVirtualAddress) dst;
+ range.length = *dst_size;
+ transferCount = *dst_size;
+ isWrite = false;
+ }
+ else
+ {
+ range.address = (IOVirtualAddress) src;
+ range.length = src_size;
+ transferCount = src_size;
+ isWrite = true;
+ }
+
+ seqNumber = 0;
+ ioReturnValue = (*cdbCommandInterface)->
+ setAndExecuteCommand (cdbCommandInterface, &cdb, transferCount,
+ &range, 1, isWrite, sane_scsicmd_timeout * 1000,
+ 0, 0, 0, &seqNumber);
+ if (ioReturnValue != kIOReturnSuccess &&
+ ioReturnValue != kIOReturnUnderrun)
+ {
+ DBG (5, "Error executing CDB command (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ ioReturnValue = (*cdbCommandInterface)->getResults (cdbCommandInterface,
+ &results);
+ if (ioReturnValue != kIOReturnSuccess &&
+ ioReturnValue != kIOReturnUnderrun)
+ {
+ DBG (5, "Error getting results from CDB Interface (0x%08x)\n",
+ ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (dst && dst_size)
+ *dst_size = results.bytesTransferred;
+
+ (*cdbCommandInterface)->Release (cdbCommandInterface);
+ (*scsiDeviceInterface)->close (scsiDeviceInterface);
+ (*scsiDeviceInterface)->Release (scsiDeviceInterface);
+
+ return SANE_STATUS_GOOD;
+ }
+
+
+ static void
+ sanei_scsi_find_devices_old_api (const char *findvendor,
+ const char *findmodel,
+ const char *findtype, int findbus,
+ int findchannel, int findid, int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ mach_port_t masterPort;
+ IOReturn ioReturnValue;
+ int i;
+ CFMutableDictionaryRef scsiMatchDictionary;
+ int deviceTypeNumber;
+ CFNumberRef deviceTypeRef;
+ io_iterator_t scsiObjectIterator;
+ io_object_t scsiDevice;
+ CFNumberRef IOUnitRef;
+ int iounit;
+ CFNumberRef scsiTargetRef;
+ int scsitarget;
+ CFNumberRef scsiLunRef;
+ int scsilun;
+ IOCFPlugInInterface **plugInInterface;
+ SInt32 score;
+ HRESULT plugInResult;
+ IOSCSIDeviceInterface **scsiDeviceInterface;
+ SCSIInquiry inquiry;
+ UInt32 inquirySize;
+ char devname[16];
+
+ masterPort = 0;
+ ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort);
+ if (ioReturnValue != kIOReturnSuccess || masterPort == 0)
+ {
+ DBG (5, "Could not get I/O master port (0x%08x)\n", ioReturnValue);
+ return;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ scsiMatchDictionary = IOServiceMatching (kIOSCSIDeviceClassName);
+ if (scsiMatchDictionary == NULL)
+ {
+ DBG (5, "Could not create SCSI matching dictionary\n");
+ return;
+ }
+ deviceTypeNumber =
+ (i == 0 ? kSCSIDevTypeScanner : kSCSIDevTypeProcessor);
+ deviceTypeRef = CFNumberCreate (NULL, kCFNumberIntType,
+ &deviceTypeNumber);
+ CFDictionarySetValue (scsiMatchDictionary,
+ CFSTR (kSCSIPropertyDeviceTypeID),
+ deviceTypeRef);
+ CFRelease (deviceTypeRef);
+
+ scsiObjectIterator = 0;
+ ioReturnValue = IOServiceGetMatchingServices (masterPort,
+ scsiMatchDictionary,
+ &scsiObjectIterator);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Could not match services (0x%08x)\n", ioReturnValue);
+ return;
+ }
+
+ while ((scsiDevice = IOIteratorNext (scsiObjectIterator)))
+ {
+ IOUnitRef =
+ IORegistryEntryCreateCFProperty (scsiDevice,
+ CFSTR (kSCSIPropertyIOUnit),
+ NULL, 0);
+ CFNumberGetValue (IOUnitRef, kCFNumberIntType, &iounit);
+ CFRelease (IOUnitRef);
+ scsiTargetRef =
+ IORegistryEntryCreateCFProperty (scsiDevice,
+ CFSTR (kSCSIPropertyTarget),
+ NULL, 0);
+ CFNumberGetValue (scsiTargetRef, kCFNumberIntType, &scsitarget);
+ CFRelease (scsiTargetRef);
+ scsiLunRef =
+ IORegistryEntryCreateCFProperty (scsiDevice,
+ CFSTR (kSCSIPropertyLun),
+ NULL, 0);
+ CFNumberGetValue (scsiLunRef, kCFNumberIntType, &scsilun);
+ CFRelease (scsiLunRef);
+
+ plugInInterface = NULL;
+ score = 0;
+ ioReturnValue =
+ IOCreatePlugInInterfaceForService (scsiDevice,
+ kIOSCSIUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ if (ioReturnValue != kIOReturnSuccess || plugInInterface == NULL)
+ {
+ DBG (5, "Error creating plugin interface (0x%08x)\n",
+ ioReturnValue);
+ return;
+ }
+
+ scsiDeviceInterface = NULL;
+ plugInResult = (*plugInInterface)->
+ QueryInterface (plugInInterface,
+ CFUUIDGetUUIDBytes (kIOSCSIDeviceInterfaceID),
+ (LPVOID) & scsiDeviceInterface);
+ if (plugInResult != S_OK || scsiDeviceInterface == NULL)
+ {
+ DBG (5, "Couldn't create SCSI device interface (%ld)\n",
+ plugInResult);
+ return;
+ }
+
+ (*plugInInterface)->Release (plugInInterface);
+ IOObjectRelease (scsiDevice);
+
+ ioReturnValue = (*scsiDeviceInterface)->
+ getInquiryData (scsiDeviceInterface, &inquiry,
+ sizeof (SCSIInquiry), &inquirySize);
+
+ (*scsiDeviceInterface)->Release (scsiDeviceInterface);
+
+ if ((findlun < 0 || findlun == scsilun) &&
+ (findvendor == NULL || strncmp (findvendor,
+ inquiry.vendorName,
+ strlen (findvendor)) == 0) &&
+ (findmodel == NULL || strncmp (findmodel,
+ inquiry.productName,
+ strlen (findmodel)) == 0))
+ {
+ sprintf (devname, "u%dt%dl%d", iounit, scsitarget, scsilun);
+ (*attach) (devname);
+ }
+ }
+ IOObjectRelease (scsiObjectIterator);
+ }
+ }
+
+# endif /* ifdef HAVE_IOKIT_CDB_IOSCSILIB_H */
+
+# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \
+ defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H)
+
+ static
+ void CreateMatchingDictionaryForSTUC (SInt32 peripheralDeviceType,
+ const char *findvendor,
+ const char *findmodel,
+ const CFDataRef scsiguid,
+ CFMutableDictionaryRef * matchingDict)
+ {
+ CFMutableDictionaryRef subDict;
+ CFNumberRef deviceTypeRef;
+ CFStringRef str;
+
+ /* Create the dictionaries */
+ *matchingDict =
+ CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (*matchingDict == NULL)
+ {
+ return;
+ }
+
+ subDict =
+ CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (subDict == NULL)
+ {
+ CFRelease (*matchingDict);
+ *matchingDict = NULL;
+ return;
+ }
+
+ /* Create a dictionary with the "SCSITaskDeviceCategory" key with the
+ appropriate value for the device type we're interested in.*/
+
+ CFDictionarySetValue (subDict,
+ CFSTR (kIOPropertySCSITaskDeviceCategory),
+ CFSTR (kIOPropertySCSITaskUserClientDevice));
+
+ deviceTypeRef = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType,
+ &peripheralDeviceType);
+ CFDictionarySetValue (subDict,
+ CFSTR (kIOPropertySCSIPeripheralDeviceType),
+ deviceTypeRef);
+ CFRelease (deviceTypeRef);
+
+ /* Add search for a vendor or model */
+
+ if (findvendor)
+ {
+ str = CFStringCreateWithCString (kCFAllocatorDefault, findvendor,
+ kCFStringEncodingUTF8);
+ CFDictionarySetValue (subDict,
+ CFSTR (kIOPropertySCSIVendorIdentification),
+ str);
+ CFRelease (str);
+ }
+ if (findmodel)
+ {
+ str = CFStringCreateWithCString (kCFAllocatorDefault, findmodel,
+ kCFStringEncodingUTF8);
+ CFDictionarySetValue (subDict,
+ CFSTR (kIOPropertySCSIProductIdentification),
+ str);
+ CFRelease (str);
+ }
+ if (scsiguid)
+ {
+ CFDictionarySetValue (subDict,
+ CFSTR
+ (kIOPropertySCSITaskUserClientInstanceGUID),
+ scsiguid);
+ }
+
+ /* Add the dictionary to the main dictionary with the key "IOPropertyMatch"
+ to narrow the search to the above dictionary. */
+
+ CFDictionarySetValue (*matchingDict, CFSTR (kIOPropertyMatchKey), subDict);
+ CFRelease (subDict);
+ }
+
+ static
+ void CreateDeviceInterfaceUsingSTUC (io_object_t scsiDevice,
+ IOCFPlugInInterface ***
+ thePlugInInterface,
+ SCSITaskDeviceInterface ***
+ theInterface)
+ {
+ IOReturn ioReturnValue;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ SInt32 score = 0;
+ HRESULT plugInResult;
+ SCSITaskDeviceInterface **interface = NULL;
+
+ /* Create the base interface of type IOCFPlugInInterface.
+ This object will be used to create the SCSI device interface object. */
+
+ ioReturnValue =
+ IOCreatePlugInInterfaceForService (scsiDevice,
+ kIOSCSITaskDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error creating plugin interface (0x%08x)\n", ioReturnValue);
+ return;
+ }
+
+ /* Query the base plugin interface for an instance of the specific
+ SCSI device interface object. */
+
+ plugInResult =
+ (*plugInInterface)->QueryInterface (plugInInterface,
+ CFUUIDGetUUIDBytes
+ (kIOSCSITaskDeviceInterfaceID),
+ (LPVOID) & interface);
+ if (plugInResult != S_OK)
+ {
+ DBG (5, "Couldn't create SCSI device interface (%ld)\n",
+ (long) plugInResult);
+ return;
+ }
+
+ /* Set the return values. */
+
+ *thePlugInInterface = plugInInterface;
+ *theInterface = interface;
+ }
+
+ static SANE_Status
+ ExecuteSCSITask (SCSITaskInterface ** task,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ SCSITaskStatus taskStatus;
+ SCSI_Sense_Data senseData;
+ SCSICommandDescriptorBlock cdb;
+ IOReturn ioReturnValue;
+#ifdef HAVE_SCSITASKSGELEMENT
+ SCSITaskSGElement range;
+#else
+ IOVirtualRange range;
+#endif
+ UInt64 transferCount = 0;
+ UInt64 data_length = 0;
+ UInt8 transferType = 0;
+
+ if (dst && dst_size) /* isRead */
+ {
+ DBG (6, "isRead dst_size:%ld\n", *dst_size);
+
+ /* Zero the buffer. */
+ bzero (dst, *dst_size);
+
+ /* Configure the virtual range for the buffer. */
+ range.address = (long) dst;
+ range.length = *dst_size;
+
+ data_length = *dst_size;
+ transferType = kSCSIDataTransfer_FromTargetToInitiator;
+ }
+ else
+ {
+ DBG (6, "isWrite src_size:%ld\n", src_size);
+
+ /* Configure the virtual range for the buffer. */
+ range.address = (long) src;
+ range.length = src_size;
+
+ data_length = src_size;
+ transferType = kSCSIDataTransfer_FromInitiatorToTarget;
+ }
+
+
+ /* zero the senseData and CDB */
+ bzero (&senseData, sizeof (senseData));
+ bzero (cdb, sizeof (cdb));
+
+ /* copy the command data */
+ memcpy (cdb, cmd, cmd_size);
+
+ /* Set the actual cdb in the task */
+ ioReturnValue = (*task)->SetCommandDescriptorBlock (task, cdb, cmd_size);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error setting CDB (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Set the scatter-gather entry in the task */
+ ioReturnValue = (*task)->SetScatterGatherEntries (task, &range, 1,
+ data_length,
+ transferType);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error setting scatter-gather entries (0x%08x)\n",
+ ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Set the timeout in the task */
+ ioReturnValue = (*task)->SetTimeoutDuration (task,
+ sane_scsicmd_timeout * 1000);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error setting timeout (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "Executing command\n");
+
+ /* Send it! */
+ ioReturnValue = (*task)->ExecuteTaskSync (task, &senseData, &taskStatus,
+ &transferCount);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error executing task (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "ExecuteTaskSync OK Transferred %lld bytes\n", transferCount);
+
+ if (taskStatus != kSCSITaskStatus_GOOD)
+ {
+ DBG (5, "taskStatus = 0x%08x\n", taskStatus);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Task worked correctly */
+ if (dst && dst_size)
+ *dst_size = transferCount;
+
+ return SANE_STATUS_GOOD;
+ }
+
+ static SANE_Status
+ ExecuteCommandUsingSTUC (SCSITaskDeviceInterface ** interface,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ SCSITaskInterface **task;
+ IOReturn ioReturnValue;
+ SANE_Status returnValue;
+
+ /* Get exclusive access for the device if we can. This must be done
+ before any SCSITasks can be created and sent to the device. */
+ ioReturnValue = (*interface)->ObtainExclusiveAccess (interface);
+
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "ObtainExclusiveAccess failed (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Create a task now that we have exclusive access */
+ task = (*interface)->CreateSCSITask (interface);
+
+ if (task == NULL)
+ {
+ DBG (5, "CreateSCSITask returned NULL\n");
+ (*interface)->ReleaseExclusiveAccess (interface);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ returnValue = ExecuteSCSITask (task, cmd, cmd_size,
+ src, src_size, dst, dst_size);
+
+ /* Release the task interface */
+ (*task)->Release (task);
+
+ /* Release exclusive access */
+ (*interface)->ReleaseExclusiveAccess (interface);
+
+ return returnValue;
+ }
+
+ static SANE_Status
+ sanei_scsi_cmd2_stuc_api (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ CFDataRef guid;
+ mach_port_t masterPort;
+ int i;
+ io_object_t scsiDevice;
+ SInt32 peripheralDeviceType;
+ CFMutableDictionaryRef matchingDict;
+ io_iterator_t iokIterator;
+ IOReturn ioReturnValue;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ SCSITaskDeviceInterface **interface = NULL;
+ io_object_t nextDevice;
+ SANE_Status returnValue;
+
+ guid = fd_info[fd].pdata;
+ if (!guid)
+ {
+ DBG (5, "No GUID\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (2, "cmd2: cmd_size:%ld src_size:%ld dst_size:%ld isWrite:%d\n",
+ cmd_size, src_size, (!dst_size) ? 0 : *dst_size, (!dst_size) ? 1 : 0);
+
+ /* Use default master port */
+ masterPort = 0;
+ ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort);
+ if (ioReturnValue != kIOReturnSuccess || masterPort == 0)
+ {
+ DBG (5, "Could not get I/O master port (0x%08x)\n", ioReturnValue);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Search for both Scanner type and Processor type devices */
+ /* GB TDB This should only be needed for find */
+ scsiDevice = 0;
+ for (i = 0; !scsiDevice && i < 2; i++)
+ {
+ peripheralDeviceType =
+ (i == 0 ? kINQUIRY_PERIPHERAL_TYPE_ScannerSCSI2Device :
+ kINQUIRY_PERIPHERAL_TYPE_ProcessorSPCDevice);
+
+ /* Set up a matching dictionary to search the I/O Registry for
+ the SCSI device */
+ /* we are interested in, specifying the SCSITaskUserClient GUID. */
+ matchingDict = NULL;
+ CreateMatchingDictionaryForSTUC (peripheralDeviceType, NULL, NULL,
+ guid, &matchingDict);
+ if (matchingDict == NULL)
+ {
+ DBG (5, "CreateMatchingDictionaryForSTUC Failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Now search I/O Registry for the matching device */
+ iokIterator = 0;
+ ioReturnValue =
+ IOServiceGetMatchingServices (masterPort, matchingDict,
+ &iokIterator);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "IOServiceGetMatchingServices Failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ scsiDevice = IOIteratorNext (iokIterator);
+
+ while ((nextDevice = IOIteratorNext (iokIterator)))
+ {
+ IOObjectRelease (nextDevice);
+ }
+
+ IOObjectRelease (iokIterator);
+ }
+
+ if (!scsiDevice)
+ {
+ DBG (5, "Device not found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Found Device */
+ /* Create interface */
+
+ CreateDeviceInterfaceUsingSTUC (scsiDevice, &plugInInterface, &interface);
+
+ /* Done with SCSI object from I/O Registry. */
+ ioReturnValue = IOObjectRelease (scsiDevice);
+
+ returnValue = SANE_STATUS_IO_ERROR;
+
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "Error releasing SCSI device. (0x%08x)\n", ioReturnValue);
+ }
+ else if (interface != NULL)
+ {
+ /* Execute the command */
+ returnValue =
+ ExecuteCommandUsingSTUC (interface, cmd, cmd_size, src, src_size,
+ dst, dst_size);
+ }
+
+ if (interface != NULL)
+ {
+ (*interface)->Release (interface);
+ }
+
+ if (plugInInterface != NULL)
+ {
+ IODestroyPlugInInterface (plugInInterface);
+ }
+
+ return returnValue;
+ }
+
+ static void
+ sanei_scsi_find_devices_stuc_api (const char *findvendor,
+ const char *findmodel,
+ const char *findtype, int findbus,
+ int findchannel, int findid, int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ mach_port_t masterPort;
+ IOReturn ioReturnValue;
+ int i;
+ SInt32 peripheralDeviceType;
+ CFMutableDictionaryRef matchingDict;
+ io_iterator_t iokIterator;
+ io_object_t scsiDevice;
+ CFDataRef GUIDRef;
+ char *devname;
+ int len;
+ const unsigned char *p;
+ CFDictionaryRef protocolCharacteristics;
+ CFNumberRef scsiLunRef;
+ int scsilun;
+
+ masterPort = 0;
+ ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort);
+ if (ioReturnValue != kIOReturnSuccess || masterPort == 0)
+ return;
+
+ DBG (5, "Search for Vendor: %s Model: %s\n",
+ (findvendor) ? findvendor : "(none)",
+ (findmodel) ? findmodel : "(none)");
+
+ /* Search for both Scanner type and Processor type devices */
+
+ for (i = 0; i < 2; i++)
+ {
+ peripheralDeviceType =
+ (i == 0 ? kINQUIRY_PERIPHERAL_TYPE_ScannerSCSI2Device :
+ kINQUIRY_PERIPHERAL_TYPE_ProcessorSPCDevice);
+
+ /* Set up a matching dictionary to search the I/O Registry for SCSI
+ devices we are interested in. */
+
+ matchingDict = NULL;
+ CreateMatchingDictionaryForSTUC (peripheralDeviceType, findvendor,
+ findmodel, NULL, &matchingDict);
+ if (matchingDict == NULL)
+ {
+ DBG (5, "CreateMatchingDictionaryForSTUC Failed\n");
+ return;
+ }
+
+ /* Now search I/O Registry for matching devices. */
+
+ iokIterator = 0;
+ ioReturnValue =
+ IOServiceGetMatchingServices (masterPort, matchingDict,
+ &iokIterator);
+ if (ioReturnValue != kIOReturnSuccess)
+ {
+ DBG (5, "IOServiceGetMatchingServices Failed\n");
+ return;
+ }
+
+ /* Check devices */
+
+ while ((scsiDevice = IOIteratorNext (iokIterator)))
+ {
+ scsilun = 0;
+ protocolCharacteristics = IORegistryEntryCreateCFProperty
+ (scsiDevice, CFSTR ("Protocol Characteristics"), NULL, 0);
+ if (protocolCharacteristics)
+ {
+ scsiLunRef = CFDictionaryGetValue
+ (protocolCharacteristics,
+ CFSTR ("SCSI Logical Unit Number"));
+ if (scsiLunRef)
+ CFNumberGetValue (scsiLunRef, kCFNumberIntType, &scsilun);
+ CFRelease (protocolCharacteristics);
+ }
+
+ if (findlun < 0 || findlun == scsilun)
+ {
+ /* Create device name from the SCSITaskUserClient GUID */
+
+ GUIDRef = IORegistryEntryCreateCFProperty
+ (scsiDevice,
+ CFSTR (kIOPropertySCSITaskUserClientInstanceGUID),
+ NULL, 0);
+
+ if (GUIDRef)
+ {
+ len = CFDataGetLength (GUIDRef);
+ p = CFDataGetBytePtr (GUIDRef);
+
+ devname = (char *) malloc (2 * len + 3);
+ devname [0] = '<';
+ for (i = 0; i < len; i++)
+ sprintf (&devname [2 * i + 1], "%02x", p [i]);
+ devname [2 * len + 1] = '>';
+ devname [2 * len + 2] = '\0';
+
+ CFRelease (GUIDRef);
+
+ DBG (1, "Found: %s\n", devname);
+
+ /* Attach to the device */
+ (*attach) (devname);
+ free (devname);
+ }
+ else
+ DBG (1, "Can't find SCSITaskUserClient GUID\n");
+ }
+ }
+ IOObjectRelease (iokIterator);
+ }
+ }
+
+# endif /* HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H */
+
+ SANE_Status
+ sanei_scsi_cmd2 (int fd,
+ const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ if (fd_info[fd].pdata)
+# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \
+ defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H)
+ return sanei_scsi_cmd2_stuc_api (fd, cmd, cmd_size, src, src_size,
+ dst, dst_size);
+# else
+ return SANE_STATUS_INVAL;
+# endif
+ else
+# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H
+ return sanei_scsi_cmd2_old_api (fd, cmd, cmd_size, src, src_size,
+ dst, dst_size);
+# else
+ return SANE_STATUS_INVAL;
+# endif
+ }
+
+ void
+ sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \
+ defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H)
+ sanei_scsi_find_devices_stuc_api (findvendor, findmodel, findtype,
+ findbus, findchannel, findid,
+ findlun, attach);
+# endif
+# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H
+ sanei_scsi_find_devices_old_api (findvendor, findmodel, findtype,
+ findbus, findchannel, findid,
+ findlun, attach);
+# endif
+ }
+
+#define WE_HAVE_FIND_DEVICES
+
+#endif /* USE == MACOSX_INTERFACE */
+
+
+#ifndef WE_HAVE_ASYNC_SCSI
+
+ SANE_Status
+ sanei_scsi_req_enter2 (int fd, const void *cmd, size_t cmd_size,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+ {
+ return sanei_scsi_cmd2 (fd, cmd, cmd_size, src, src_size, dst, dst_size);
+ }
+
+ SANE_Status sanei_scsi_req_wait (void *id)
+ {
+ return SANE_STATUS_GOOD;
+ }
+
+ void sanei_scsi_req_flush_all (void)
+ {
+ }
+
+ void sanei_scsi_req_flush_all_extended (int fd)
+ {
+ }
+
+#endif /* WE_HAVE_ASYNC_SCSI */
+
+ SANE_Status sanei_scsi_req_enter (int fd,
+ const void *src, size_t src_size,
+ void *dst, size_t * dst_size, void **idp)
+ {
+ size_t cmd_size = CDB_SIZE (*(const char *) src);
+
+ if (dst_size && *dst_size)
+ assert (src_size == cmd_size);
+ else
+ assert (src_size >= cmd_size);
+
+ return sanei_scsi_req_enter2 (fd, src, cmd_size,
+ (const char *) src + cmd_size,
+ src_size - cmd_size, dst, dst_size, idp);
+ }
+
+ SANE_Status
+ sanei_scsi_cmd (int fd, const void *src, size_t src_size,
+ void *dst, size_t * dst_size)
+ {
+ size_t cmd_size = CDB_SIZE (*(const char *) src);
+
+ if (dst_size && *dst_size)
+ assert (src_size == cmd_size);
+ else
+ assert (src_size >= cmd_size);
+
+ return sanei_scsi_cmd2 (fd, src, cmd_size,
+ (const char *) src + cmd_size,
+ src_size - cmd_size, dst, dst_size);
+ }
+
+
+
+#ifndef WE_HAVE_FIND_DEVICES
+
+ void
+ sanei_scsi_find_devices (const char *findvendor, const char *findmodel,
+ const char *findtype,
+ int findbus, int findchannel, int findid,
+ int findlun,
+ SANE_Status (*attach) (const char *dev))
+ {
+ DBG_INIT ();
+ DBG (1, "sanei_scsi_find_devices: not implemented for this platform\n");
+ }
+
+#endif /* WE_HAVE_FIND_DEVICES */
diff --git a/sanei/sanei_tcp.c b/sanei/sanei_tcp.c
new file mode 100644
index 0000000..a57d7c7
--- /dev/null
+++ b/sanei/sanei_tcp.c
@@ -0,0 +1,136 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2006 Tower Technologies
+ Author: Alessandro Zummo <a.zummo@towertech.it>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#define BACKEND_NAME sanei_tcp
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_tcp.h"
+
+SANE_Status
+sanei_tcp_open(const char *host, int port, int *fdp)
+{
+ int fd, err;
+ struct sockaddr_in saddr;
+ struct hostent *h;
+#ifdef HAVE_WINSOCK2_H
+ WSADATA wsaData;
+#endif
+
+ DBG_INIT();
+ DBG(1, "%s: host = %s, port = %d\n", __FUNCTION__, host, port);
+
+#ifdef HAVE_WINSOCK2_H
+ err = WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (err != 0)
+ return SANE_STATUS_INVAL;
+#endif
+
+ h = gethostbyname(host);
+
+ if (h == NULL || h->h_addr_list[0] == NULL
+ || h->h_addrtype != AF_INET)
+ return SANE_STATUS_INVAL;
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ return SANE_STATUS_INVAL;
+
+ memset(&saddr, 0x00, sizeof(struct sockaddr_in));
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(port);
+ memcpy(&saddr.sin_addr, h->h_addr_list[0], h->h_length);
+
+ if ((err =
+ connect(fd, (struct sockaddr *) &saddr,
+ sizeof(struct sockaddr_in))) != 0) {
+ close(fd);
+ return SANE_STATUS_INVAL;
+ }
+
+ *fdp = fd;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_tcp_close(int fd)
+{
+ close(fd);
+#ifdef HAVE_WINSOCK2_H
+ WSACleanup();
+#endif
+}
+
+ssize_t
+sanei_tcp_write(int fd, const u_char * buf, int count)
+{
+ return send(fd, buf, count, 0);
+}
+
+ssize_t
+sanei_tcp_read(int fd, u_char * buf, int count)
+{
+ ssize_t bytes_recv = 0, rc = 1;
+
+ while (bytes_recv < count && rc > 0)
+ {
+ rc = recv(fd, buf+bytes_recv, count-bytes_recv, 0);
+ if (rc > 0)
+ bytes_recv += rc;
+
+ }
+ return bytes_recv;
+}
diff --git a/sanei/sanei_thread.c b/sanei/sanei_thread.c
new file mode 100644
index 0000000..fd58af2
--- /dev/null
+++ b/sanei/sanei_thread.c
@@ -0,0 +1,562 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1998-2001 Yuri Dario
+ Copyright (C) 2003-2004 Gerhard Jaeger (pthread/process 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ OS/2
+ Helper functions for the OS/2 port (using threads instead of forked
+ processes). Don't use them in the backends, they are used automatically by
+ macros.
+
+ Other OS:
+ use this lib, if you intend to let run your reader function within its own
+ task (thread or process). Depending on the OS and/or the configure settings
+ pthread or fork is used to achieve this goal.
+*/
+
+#include "../include/sane/config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_OS2_H
+# define INCL_DOSPROCESS
+# include <os2.h>
+#endif
+#ifdef __BEOS__
+# undef USE_PTHREAD /* force */
+# include <kernel/OS.h>
+#endif
+#if !defined USE_PTHREAD && !defined HAVE_OS2_H && !defined __BEOS__
+# include <sys/wait.h>
+#endif
+#if defined USE_PTHREAD
+# include <pthread.h>
+#endif
+
+#define BACKEND_NAME sanei_thread /**< name of this module for debugging */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifndef _VAR_NOT_USED
+# define _VAR_NOT_USED(x) ((x)=(x))
+#endif
+
+typedef struct {
+
+ int (*func)( void* );
+ SANE_Status status;
+ void *func_data;
+
+} ThreadDataDef, *pThreadDataDef;
+
+static ThreadDataDef td;
+
+/** for init issues - here only for the debug output
+ */
+void
+sanei_thread_init( void )
+{
+ DBG_INIT();
+
+ memset( &td, 0, sizeof(ThreadDataDef));
+ td.status = SANE_STATUS_GOOD;
+}
+
+SANE_Bool
+sanei_thread_is_forked( void )
+{
+#if defined USE_PTHREAD || defined HAVE_OS2_H || defined __BEOS__
+ return SANE_FALSE;
+#else
+ return SANE_TRUE;
+#endif
+}
+
+/* Use this to mark a SANE_Pid as invaild instead of marking with -1.
+ */
+static void
+sanei_thread_set_invalid( SANE_Pid *pid )
+{
+
+#ifdef WIN32
+#ifdef WINPTHREAD_API
+ *pid = 0;
+#else
+ pid->p = 0;
+#endif
+#else
+ *pid = -1;
+#endif
+}
+
+/* Return if PID is a valid PID or not. */
+SANE_Bool
+sanei_thread_is_invalid( SANE_Pid pid )
+{
+ SANE_Bool rc = SANE_FALSE;
+
+#ifdef WIN32
+#ifdef WINPTHREAD_API
+ if (pid == 0)
+#else
+ if (pid.p == 0)
+#endif
+ rc = SANE_TRUE;
+#else
+ if (pid == -1)
+ rc = SANE_TRUE;
+#endif
+
+ return rc;
+}
+
+/* pthread_t is not an integer on all platform. Do our best to return
+ * a PID-like value from structure. On platforms were it is an integer,
+ * return that.
+ */
+static long
+sanei_thread_pid_to_long( SANE_Pid pid )
+{
+ int rc;
+
+#ifdef WIN32
+#ifdef WINPTHREAD_API
+ rc = pid;
+#else
+ rc = pid.p;
+#endif
+#else
+ rc = pid;
+#endif
+
+ return rc;
+}
+
+int
+sanei_thread_kill( SANE_Pid pid )
+{
+ DBG(2, "sanei_thread_kill() will kill %ld\n",
+ sanei_thread_pid_to_long(pid));
+#ifdef USE_PTHREAD
+#if defined (__APPLE__) && defined (__MACH__)
+ return pthread_kill((pthread_t)pid, SIGUSR2);
+#else
+ return pthread_cancel((pthread_t)pid);
+#endif
+#elif defined HAVE_OS2_H
+ return DosKillThread(pid);
+#else
+ return kill( pid, SIGTERM );
+#endif
+}
+
+#ifdef HAVE_OS2_H
+
+static void
+local_thread( void *arg )
+{
+ pThreadDataDef ltd = (pThreadDataDef)arg;
+
+ DBG( 2, "thread started, calling func() now...\n" );
+ ltd->status = ltd->func( ltd->func_data );
+
+ DBG( 2, "func() done - status = %d\n", ltd->status );
+ _endthread();
+}
+
+/*
+ * starts a new thread or process
+ * parameters:
+ * star address of reader function
+ * args pointer to scanner data structure
+ *
+ */
+SANE_Pid
+sanei_thread_begin( int (*func)(void *args), void* args )
+{
+ SANE_Pid pid;
+
+ td.func = func;
+ td.func_data = args;
+
+ pid = _beginthread( local_thread, NULL, 1024*1024, (void*)&td );
+ if ( pid == -1 ) {
+ DBG( 1, "_beginthread() failed\n" );
+ return -1;
+ }
+
+ DBG( 2, "_beginthread() created thread %d\n", pid );
+ return pid;
+}
+
+SANE_Pid
+sanei_thread_waitpid( SANE_Pid pid, int *status )
+{
+ if (status)
+ *status = 0;
+ return pid; /* DosWaitThread( (TID*) &pid, DCWW_WAIT);*/
+}
+
+int
+sanei_thread_sendsig( SANE_Pid pid, int sig )
+{
+ return 0;
+}
+
+#elif defined __BEOS__
+
+static int32
+local_thread( void *arg )
+{
+ pThreadDataDef ltd = (pThreadDataDef)arg;
+
+ DBG( 2, "thread started, calling func() now...\n" );
+ ltd->status = ltd->func( ltd->func_data );
+
+ DBG( 2, "func() done - status = %d\n", ltd->status );
+ return ltd->status;
+}
+
+/*
+ * starts a new thread or process
+ * parameters:
+ * star address of reader function
+ * args pointer to scanner data structure
+ *
+ */
+SANE_Pid
+sanei_thread_begin( int (*func)(void *args), void* args )
+{
+ SANE_Pid pid;
+
+ td.func = func;
+ td.func_data = args;
+
+ pid = spawn_thread( local_thread, "sane thread (yes they can be)", B_NORMAL_PRIORITY, (void*)&td );
+ if ( pid < B_OK ) {
+ DBG( 1, "spawn_thread() failed\n" );
+ return -1;
+ }
+ if ( resume_thread(pid) < B_OK ) {
+ DBG( 1, "resume_thread() failed\n" );
+ return -1;
+ }
+
+ DBG( 2, "spawn_thread() created thread %d\n", pid );
+ return pid;
+}
+
+SANE_Pid
+sanei_thread_waitpid( SANE_Pid pid, int *status )
+{
+ int32 st;
+ if ( wait_for_thread(pid, &st) < B_OK )
+ return -1;
+ if ( status )
+ *status = (int)st;
+ return pid;
+}
+
+int
+sanei_thread_sendsig( SANE_Pid pid, int sig )
+{
+ if (sig == SIGKILL)
+ sig = SIGKILLTHR;
+ return kill(pid, sig);
+}
+
+#else /* HAVE_OS2_H, __BEOS__ */
+
+#ifdef USE_PTHREAD
+
+/* seems to be undefined in MacOS X */
+#ifndef PTHREAD_CANCELED
+# define PTHREAD_CANCELED ((void *) -1)
+#endif
+
+/**
+ */
+#if defined (__APPLE__) && defined (__MACH__)
+static void
+thread_exit_handler( int signo )
+{
+ DBG( 2, "signal(%i) caught, calling pthread_exit now...\n", signo );
+ pthread_exit( PTHREAD_CANCELED );
+}
+#endif
+
+
+static void*
+local_thread( void *arg )
+{
+ static int status;
+ pThreadDataDef ltd = (pThreadDataDef)arg;
+
+#if defined (__APPLE__) && defined (__MACH__)
+ struct sigaction act;
+
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+ act.sa_handler = thread_exit_handler;
+ sigaction( SIGUSR2, &act, 0 );
+#else
+ int old;
+
+ pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &old );
+ pthread_setcanceltype ( PTHREAD_CANCEL_ASYNCHRONOUS, &old );
+#endif
+
+ DBG( 2, "thread started, calling func() now...\n" );
+
+ status = ltd->func( ltd->func_data );
+
+ /* so sanei_thread_get_status() will work correctly... */
+ ltd->status = status;
+
+ DBG( 2, "func() done - status = %d\n", status );
+
+ /* return the status, so pthread_join is able to get it*/
+ pthread_exit((void*)&status );
+}
+
+/**
+ */
+static void
+restore_sigpipe( void )
+{
+#ifdef SIGPIPE
+ struct sigaction act;
+
+ if( sigaction( SIGPIPE, NULL, &act ) == 0 ) {
+
+ if( act.sa_handler == SIG_IGN ) {
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = 0;
+ act.sa_handler = SIG_DFL;
+
+ DBG( 2, "restoring SIGPIPE to SIG_DFL\n" );
+ sigaction( SIGPIPE, &act, NULL );
+ }
+ }
+#endif
+}
+
+#else /* the process stuff */
+
+static int
+eval_wp_result( SANE_Pid pid, int wpres, int pf )
+{
+ int retval = SANE_STATUS_IO_ERROR;
+
+ if( wpres == pid ) {
+
+ if( WIFEXITED(pf)) {
+ retval = WEXITSTATUS(pf);
+ } else {
+
+ if( !WIFSIGNALED(pf)) {
+ retval = SANE_STATUS_GOOD;
+ } else {
+ DBG( 1, "Child terminated by signal %d\n", WTERMSIG(pf));
+ if( WTERMSIG(pf) == SIGTERM )
+ retval = SANE_STATUS_GOOD;
+ }
+ }
+ }
+ return retval;
+}
+#endif
+
+SANE_Pid
+sanei_thread_begin( int (func)(void *args), void* args )
+{
+#ifdef USE_PTHREAD
+ int result;
+ pthread_t thread;
+#ifdef SIGPIPE
+ struct sigaction act;
+
+ /* if signal handler for SIGPIPE is SIG_DFL, replace by SIG_IGN */
+ if( sigaction( SIGPIPE, NULL, &act ) == 0 ) {
+
+ if( act.sa_handler == SIG_DFL ) {
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = 0;
+ act.sa_handler = SIG_IGN;
+
+ DBG( 2, "setting SIGPIPE to SIG_IGN\n" );
+ sigaction( SIGPIPE, &act, NULL );
+ }
+ }
+#endif
+
+ td.func = func;
+ td.func_data = args;
+
+ result = pthread_create( &thread, NULL, local_thread, &td );
+ usleep( 1 );
+
+ if ( result != 0 ) {
+ DBG( 1, "pthread_create() failed with %d\n", result );
+ sanei_thread_set_invalid(&thread);
+ }
+ else
+ DBG( 2, "pthread_create() created thread %ld\n",
+ (SANE_Pid)thread );
+
+ return (SANE_Pid)thread;
+#else
+ SANE_Pid pid;
+ pid = fork();
+ if( pid < 0 ) {
+ DBG( 1, "fork() failed\n" );
+ return -1;
+ }
+
+ if( pid == 0 ) {
+
+ /* run in child context... */
+ int status = func( args );
+
+ /* don't use exit() since that would run the atexit() handlers */
+ _exit( status );
+ }
+
+ /* parents return */
+ return pid;
+#endif
+}
+
+int
+sanei_thread_sendsig( SANE_Pid pid, int sig )
+{
+ DBG(2, "sanei_thread_sendsig() %d to thread (id=%ld)\n", sig,
+ sanei_thread_pid_to_long(pid));
+#ifdef USE_PTHREAD
+ return pthread_kill( (pthread_t)pid, sig );
+#else
+ return kill( pid, sig );
+#endif
+}
+
+SANE_Pid
+sanei_thread_waitpid( SANE_Pid pid, int *status )
+{
+#ifdef USE_PTHREAD
+ int *ls;
+#else
+ int ls;
+#endif
+ SANE_Pid result;
+ int stat;
+
+ stat = 0;
+
+ DBG(2, "sanei_thread_waitpid() - %ld\n",
+ sanei_thread_pid_to_long(pid));
+#ifdef USE_PTHREAD
+ int rc;
+ rc = pthread_join( (pthread_t)pid, (void*)&ls );
+
+ if( 0 == rc ) {
+ if( PTHREAD_CANCELED == ls ) {
+ DBG(2, "* thread has been canceled!\n" );
+ stat = SANE_STATUS_GOOD;
+ } else {
+ stat = *ls;
+ }
+ DBG(2, "* result = %d (%p)\n", stat, (void*)status );
+ result = pid;
+ }
+ /* call detach in any case to make sure that the thread resources
+ * will be freed, when the thread has terminated
+ */
+ DBG(2, "* detaching thread(%ld)\n", pid );
+ pthread_detach((pthread_t)pid);
+ if (status)
+ *status = stat;
+
+ restore_sigpipe();
+#else
+ result = waitpid( pid, &ls, 0 );
+ if((result < 0) && (errno == ECHILD)) {
+ stat = SANE_STATUS_GOOD;
+ result = pid;
+ } else {
+ stat = eval_wp_result( pid, result, ls );
+ DBG(2, "* result = %d (%p)\n", stat, (void*)status );
+ }
+ if( status )
+ *status = stat;
+#endif
+ return result;
+}
+
+#endif /* HAVE_OS2_H */
+
+SANE_Status
+sanei_thread_get_status( SANE_Pid pid )
+{
+#if defined USE_PTHREAD || defined HAVE_OS2_H || defined __BEOS__
+ _VAR_NOT_USED( pid );
+
+ return td.status;
+#else
+ int ls, stat, result;
+
+ stat = SANE_STATUS_IO_ERROR;
+ if( pid > 0 ) {
+
+ result = waitpid( pid, &ls, WNOHANG );
+
+ stat = eval_wp_result( pid, result, ls );
+ }
+ return stat;
+#endif
+}
+
+/* END sanei_thread.c .......................................................*/
diff --git a/sanei/sanei_udp.c b/sanei/sanei_udp.c
new file mode 100644
index 0000000..ab316ea
--- /dev/null
+++ b/sanei/sanei_udp.c
@@ -0,0 +1,232 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2006 Tower Technologies
+ Author: Alessandro Zummo <a.zummo@towertech.it>
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+
+#define BACKEND_NAME sanei_udp
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_udp.h"
+
+static SANE_Status
+sanei_udp_socket(int *fdp, int broadcast)
+{
+ int fd;
+
+ if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ return SANE_STATUS_INVAL;
+
+ if (broadcast) {
+ int opt = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
+ (char *) &opt, sizeof(opt)) < 0) {
+ close(fd);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ *fdp = fd;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+sanei_udp_connect(int fd, const char *host, int port)
+{
+ int err;
+ struct sockaddr_in saddr;
+ struct hostent *h;
+
+ h = gethostbyname(host);
+
+ if (h == NULL || h->h_addr_list[0] == NULL
+ || h->h_addrtype != AF_INET)
+ return SANE_STATUS_INVAL;
+
+ memset(&saddr, 0x00, sizeof(struct sockaddr_in));
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(port);
+ memcpy(&saddr.sin_addr, h->h_addr_list[0], h->h_length);
+
+ if ((err = connect(fd, (struct sockaddr *) &saddr,
+ sizeof(struct sockaddr_in))) != 0) {
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_udp_open(const char *host, int port, int *fdp)
+{
+ int status;
+#ifdef HAVE_WINSOCK2_H
+ WSADATA wsaData;
+#endif
+
+ DBG_INIT();
+ DBG(1, "%s\n", __func__);
+
+#ifdef HAVE_WINSOCK2_H
+ status = WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (status != 0)
+ return SANE_STATUS_INVAL;
+#endif
+
+ status = sanei_udp_socket(fdp, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = sanei_udp_connect(*fdp, host, port);
+ if (status != SANE_STATUS_GOOD) {
+ close(*fdp);
+ return status;
+ }
+
+ return status;
+}
+
+SANE_Status
+sanei_udp_open_broadcast(int *fdp)
+{
+ int status;
+
+ DBG_INIT();
+ DBG(1, "%s\n", __func__);
+
+ status = sanei_udp_socket(fdp, 1);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ return status;
+}
+
+void
+sanei_udp_close(int fd)
+{
+ close(fd);
+#ifdef HAVE_WINSOCK2_H
+ WSACleanup();
+#endif
+}
+
+ssize_t
+sanei_udp_write(int fd, const u_char * buf, int count)
+{
+ return send(fd, buf, count, 0);
+}
+
+ssize_t
+sanei_udp_write_broadcast(int fd, int port, const u_char * buf, int count)
+{
+ struct sockaddr_in saddr;
+
+ memset(&saddr, 0x00, sizeof(struct sockaddr_in));
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(port);
+ saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+
+ return sendto(fd, buf, count, 0,
+ (struct sockaddr *)&saddr, sizeof(saddr));
+}
+
+void
+sanei_udp_set_nonblock(int fd, SANE_Bool nonblock)
+{
+#ifdef HAVE_WINSOCK2_H
+ u_long mode=nonblock;
+
+ ioctlsocket(fd, FIONBIO, &mode);
+#else
+ long flags;
+
+ flags = fcntl(fd, F_GETFL, 0L);
+ if (nonblock)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+ fcntl(fd, F_SETFL, flags);
+#endif
+}
+
+ssize_t
+sanei_udp_read(int fd, u_char * buf, int count)
+{
+ return recv(fd, buf, count, 0);
+}
+
+ssize_t
+sanei_udp_recvfrom(int fd, u_char * buf, int count, char **fromp)
+{
+ ssize_t l;
+ socklen_t fl;
+ struct sockaddr_in from;
+
+ fl = sizeof(from);
+
+ l = recvfrom(fd, buf, count, 0, (struct sockaddr *) &from, &fl);
+
+ if (l > 0 && fromp) {
+ *fromp = inet_ntoa(from.sin_addr);
+ }
+
+ return l;
+}
+
diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c
new file mode 100644
index 0000000..7401658
--- /dev/null
+++ b/sanei/sanei_usb.c
@@ -0,0 +1,3151 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001 - 2005 Henning Meier-Geinitz
+ Copyright (C) 2001 Frank Zago (sanei_usb_control_msg)
+ Copyright (C) 2003 Rene Rebe (sanei_read_int,sanei_set_timeout)
+ Copyright (C) 2005 Paul Smedley <paul@smedley.info> (OS/2 usbcalls)
+ Copyright (C) 2008 m. allan noah (bus rescan support, sanei_usb_clear_halt)
+ Copyright (C) 2009 Julien BLACHE <jb@jblache.org> (libusb-1.0)
+ Copyright (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> (sanei_usb_set_endpoint)
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ This file provides a generic USB interface. */
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
+
+/* for debug messages */
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ "<unknown>"
+# endif
+#endif
+
+
+#ifdef HAVE_RESMGR
+#include <resmgr.h>
+#endif
+
+#ifdef HAVE_LIBUSB
+#ifdef HAVE_LUSB0_USB_H
+#include <lusb0_usb.h>
+#else
+#include <usb.h>
+#endif
+#endif /* HAVE_LIBUSB */
+
+#ifdef HAVE_LIBUSB_1_0
+#include <libusb.h>
+#endif /* HAVE_LIBUSB_1_0 */
+
+#ifdef HAVE_USBCALLS
+#include <usb.h>
+#include <os2.h>
+#include <usbcalls.h>
+#define MAX_RW 64000
+static int usbcalls_timeout = 30 * 1000; /* 30 seconds */
+USBHANDLE dh;
+PHEV pUsbIrqStartHev=NULL;
+
+static
+struct usb_descriptor_header *
+GetNextDescriptor( struct usb_descriptor_header *currHead, UCHAR *lastBytePtr)
+{
+ UCHAR *currBytePtr, *nextBytePtr;
+
+ if (!currHead->bLength)
+ return (NULL);
+ currBytePtr=(UCHAR *)currHead;
+ nextBytePtr=currBytePtr+currHead->bLength;
+ if (nextBytePtr>=lastBytePtr)
+ return (NULL);
+ return ((struct usb_descriptor_header*)nextBytePtr);
+}
+#endif /* HAVE_USBCALLS */
+
+#if (defined (__FreeBSD__) && (__FreeBSD_version < 800064))
+#include <sys/param.h>
+#include <dev/usb/usb.h>
+#endif /* __FreeBSD__ */
+#if defined (__DragonFly__)
+#include <bus/usb/usb.h>
+#endif
+
+#define BACKEND_NAME sanei_usb
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_config.h"
+
+typedef enum
+{
+ sanei_usb_method_scanner_driver = 0, /* kernel scanner driver
+ (Linux, BSD) */
+ sanei_usb_method_libusb,
+
+ sanei_usb_method_usbcalls
+}
+sanei_usb_access_method_type;
+
+typedef struct
+{
+ SANE_Bool open;
+ sanei_usb_access_method_type method;
+ int fd;
+ SANE_String devname;
+ SANE_Int vendor;
+ SANE_Int product;
+ SANE_Int bulk_in_ep;
+ SANE_Int bulk_out_ep;
+ SANE_Int iso_in_ep;
+ SANE_Int iso_out_ep;
+ SANE_Int int_in_ep;
+ SANE_Int int_out_ep;
+ SANE_Int control_in_ep;
+ SANE_Int control_out_ep;
+ SANE_Int interface_nr;
+ SANE_Int missing;
+#ifdef HAVE_LIBUSB
+ usb_dev_handle *libusb_handle;
+ struct usb_device *libusb_device;
+#endif /* HAVE_LIBUSB */
+#ifdef HAVE_LIBUSB_1_0
+ libusb_device *lu_device;
+ libusb_device_handle *lu_handle;
+#endif /* HAVE_LIBUSB_1_0 */
+}
+device_list_type;
+
+/**
+ * total number of devices that can be found at the same time */
+#define MAX_DEVICES 100
+
+/**
+ * per-device information, using the functions' parameters dn as index */
+static device_list_type devices[MAX_DEVICES];
+
+/**
+ * total number of detected devices in devices array */
+static int device_number=0;
+
+/**
+ * count number of time sanei_usb has been initialized */
+static int initialized=0;
+
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+static int libusb_timeout = 30 * 1000; /* 30 seconds */
+#endif /* HAVE_LIBUSB */
+
+#ifdef HAVE_LIBUSB_1_0
+static libusb_context *sanei_usb_ctx;
+#endif /* HAVE_LIBUSB_1_0 */
+
+#if defined (__linux__)
+/* From /usr/src/linux/driver/usb/scanner.h */
+#define SCANNER_IOCTL_VENDOR _IOR('U', 0x20, int)
+#define SCANNER_IOCTL_PRODUCT _IOR('U', 0x21, int)
+#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, devrequest)
+/* Older (unofficial) IOCTL numbers for Linux < v2.4.13 */
+#define SCANNER_IOCTL_VENDOR_OLD _IOR('u', 0xa0, int)
+#define SCANNER_IOCTL_PRODUCT_OLD _IOR('u', 0xa1, int)
+
+/* From /usr/src/linux/include/linux/usb.h */
+typedef struct
+{
+ unsigned char requesttype;
+ unsigned char request;
+ unsigned short value;
+ unsigned short index;
+ unsigned short length;
+}
+devrequest;
+
+/* From /usr/src/linux/driver/usb/scanner.h */
+struct ctrlmsg_ioctl
+{
+ devrequest req;
+ void *data;
+}
+cmsg;
+#elif defined(__BEOS__)
+#include <drivers/USB_scanner.h>
+#include <kernel/OS.h>
+#endif /* __linux__ */
+
+/* Debug level from sanei_init_debug */
+static SANE_Int debug_level;
+
+static void
+print_buffer (const SANE_Byte * buffer, SANE_Int size)
+{
+#define NUM_COLUMNS 16
+#define PRINT_BUFFER_SIZE (4 + NUM_COLUMNS * (3 + 1) + 1 + 1)
+ char line_str[PRINT_BUFFER_SIZE];
+ char *pp;
+ int column;
+ int line;
+
+ memset (line_str, 0, PRINT_BUFFER_SIZE);
+
+ for (line = 0; line < ((size + NUM_COLUMNS - 1) / NUM_COLUMNS); line++)
+ {
+ pp = line_str;
+ sprintf (pp, "%03X ", line * NUM_COLUMNS);
+ pp += 4;
+ for (column = 0; column < NUM_COLUMNS; column++)
+ {
+ if ((line * NUM_COLUMNS + column) < size)
+ sprintf (pp, "%02X ", buffer[line * NUM_COLUMNS + column]);
+ else
+ sprintf (pp, " ");
+ pp += 3;
+ }
+ for (column = 0; column < NUM_COLUMNS; column++)
+ {
+ if ((line * NUM_COLUMNS + column) < size)
+ sprintf (pp, "%c",
+ (buffer[line * NUM_COLUMNS + column] < 127) &&
+ (buffer[line * NUM_COLUMNS + column] > 31) ?
+ buffer[line * NUM_COLUMNS + column] : '.');
+ else
+ sprintf (pp, " ");
+ pp += 1;
+ }
+ DBG (11, "%s\n", line_str);
+ }
+}
+
+#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0)
+static void
+kernel_get_vendor_product (int fd, const char *name, int *vendorID, int *productID)
+{
+#if defined (__linux__)
+ /* read the vendor and product IDs via the IOCTLs */
+ if (ioctl (fd, SCANNER_IOCTL_VENDOR, vendorID) == -1)
+ {
+ if (ioctl (fd, SCANNER_IOCTL_VENDOR_OLD, vendorID) == -1)
+ DBG (3, "kernel_get_vendor_product: ioctl (vendor) "
+ "of device %s failed: %s\n", name, strerror (errno));
+ }
+ if (ioctl (fd, SCANNER_IOCTL_PRODUCT, productID) == -1)
+ {
+ if (ioctl (fd, SCANNER_IOCTL_PRODUCT_OLD, productID) == -1)
+ DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
+ "of device %s failed: %s\n", name, strerror (errno));
+ }
+#elif defined(__BEOS__)
+ {
+ uint16 vendor, product;
+ if (ioctl (fd, B_SCANNER_IOCTL_VENDOR, &vendor) != B_OK)
+ DBG (3, "kernel_get_vendor_product: ioctl (vendor) "
+ "of device %d failed: %s\n", fd, strerror (errno));
+ if (ioctl (fd, B_SCANNER_IOCTL_PRODUCT, &product) != B_OK)
+ DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
+ "of device %d failed: %s\n", fd, strerror (errno));
+ /* copy from 16 to 32 bit value */
+ *vendorID = vendor;
+ *productID = product;
+ }
+#elif (defined (__FreeBSD__) && __FreeBSD_version < 800064) || defined (__DragonFly__)
+ {
+ int controller;
+ int ctrl_fd;
+ char buf[40];
+ int dev;
+
+ for (controller = 0; ; controller++ )
+ {
+ snprintf (buf, sizeof (buf) - 1, "/dev/usb%d", controller);
+ ctrl_fd = open (buf, O_RDWR);
+
+ /* If we can not open the usb controller device, treat it
+ as the end of controller devices */
+ if (ctrl_fd < 0)
+ break;
+
+ /* Search for the scanner device on this bus */
+ for (dev = 1; dev < USB_MAX_DEVICES; dev++)
+ {
+ struct usb_device_info devInfo;
+ devInfo.udi_addr = dev;
+
+ if (ioctl (ctrl_fd, USB_DEVICEINFO, &devInfo) == -1)
+ break; /* Treat this as the end of devices for this controller */
+
+ snprintf (buf, sizeof (buf), "/dev/%s", devInfo.udi_devnames[0]);
+ if (strncmp (buf, name, sizeof (buf)) == 0)
+ {
+ *vendorID = (int) devInfo.udi_vendorNo;
+ *productID = (int) devInfo.udi_productNo;
+ close (ctrl_fd);
+ return;
+ }
+ }
+ close (ctrl_fd);
+ }
+ DBG (3, "kernel_get_vendor_product: Could not retrieve "
+ "vendor/product ID from device %s\n", name);
+ }
+#endif /* defined (__linux__), defined(__BEOS__), ... */
+ /* put more os-dependant stuff ... */
+}
+#endif /* !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) */
+
+/**
+ * store the given device in device list if it isn't already
+ * in it
+ * @param device device to store if new
+ */
+static void
+store_device (device_list_type device)
+{
+ int i = 0;
+ int pos = -1;
+
+ /* if there are already some devices present, check against
+ * them and leave if an equal one is found */
+ for (i = 0; i < device_number; i++)
+ {
+ if (devices[i].method == device.method
+ && !strcmp (devices[i].devname, device.devname)
+ && devices[i].vendor == device.vendor
+ && devices[i].product == device.product)
+ {
+ /*
+ * Need to update the LibUSB device pointer, since it might
+ * have changed after the latest USB scan.
+ */
+#ifdef HAVE_LIBUSB
+ devices[i].libusb_device = device.libusb_device;
+#endif
+#ifdef HAVE_LIBUSB_1_0
+ devices[i].lu_device = device.lu_device;
+#endif
+
+ devices[i].missing=0;
+ DBG (3, "store_device: not storing device %s\n", device.devname);
+
+ /* since devname has been created by strdup()
+ * we have to free it to avoid leaking memory */
+ free(device.devname);
+ return;
+ }
+ if (devices[i].missing >= 2)
+ pos = i;
+ }
+
+ /* reuse slot of a device now missing */
+ if(pos > -1){
+ DBG (3, "store_device: overwrite dn %d with %s\n", pos, device.devname);
+ /* we reuse the slot used by a now missing device
+ * so we free the allocated memory for the missing one */
+ if (devices[pos].devname) {
+ free(devices[pos].devname);
+ devices[pos].devname = NULL;
+ }
+ }
+ else{
+ if(device_number >= MAX_DEVICES){
+ DBG (3, "store_device: no room for %s\n", device.devname);
+ return;
+ }
+ pos = device_number;
+ device_number++;
+ DBG (3, "store_device: add dn %d with %s\n", pos, device.devname);
+ }
+ memcpy (&(devices[pos]), &device, sizeof (device));
+ devices[pos].open = SANE_FALSE;
+}
+
+#ifdef HAVE_LIBUSB_1_0
+static char *
+sanei_libusb_strerror (int errcode)
+{
+ /* Error codes & descriptions from the libusb-1.0 documentation */
+
+ switch (errcode)
+ {
+ case LIBUSB_SUCCESS:
+ return "Success (no error)";
+
+ case LIBUSB_ERROR_IO:
+ return "Input/output error";
+
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return "Invalid parameter";
+
+ case LIBUSB_ERROR_ACCESS:
+ return "Access denied (insufficient permissions)";
+
+ case LIBUSB_ERROR_NO_DEVICE:
+ return "No such device (it may have been disconnected)";
+
+ case LIBUSB_ERROR_NOT_FOUND:
+ return "Entity not found";
+
+ case LIBUSB_ERROR_BUSY:
+ return "Resource busy";
+
+ case LIBUSB_ERROR_TIMEOUT:
+ return "Operation timed out";
+
+ case LIBUSB_ERROR_OVERFLOW:
+ return "Overflow";
+
+ case LIBUSB_ERROR_PIPE:
+ return "Pipe error";
+
+ case LIBUSB_ERROR_INTERRUPTED:
+ return "System call interrupted (perhaps due to signal)";
+
+ case LIBUSB_ERROR_NO_MEM:
+ return "Insufficient memory";
+
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ return "Operation not supported or unimplemented on this platform";
+
+ case LIBUSB_ERROR_OTHER:
+ return "Other error";
+
+ default:
+ return "Unknown libusb-1.0 error code";
+ }
+
+ return "Unknown libusb-1.0 error code";
+}
+#endif /* HAVE_LIBUSB_1_0 */
+
+void
+sanei_usb_init (void)
+{
+#ifdef HAVE_LIBUSB_1_0
+ int ret;
+#endif /* HAVE_LIBUSB_1_0 */
+
+ DBG_INIT ();
+#ifdef DBG_LEVEL
+ debug_level = DBG_LEVEL;
+#else
+ debug_level = 0;
+#endif
+
+ /* if no device yet, clean up memory */
+ if(device_number==0)
+ memset (devices, 0, sizeof (devices));
+
+ /* initialize USB with old libusb library */
+#ifdef HAVE_LIBUSB
+ DBG (4, "%s: Looking for libusb devices\n", __func__);
+ usb_init ();
+#ifdef DBG_LEVEL
+ if (DBG_LEVEL > 4)
+ usb_set_debug (255);
+#endif /* DBG_LEVEL */
+#endif /* HAVE_LIBUSB */
+
+
+ /* initialize USB using libusb-1.0 */
+#ifdef HAVE_LIBUSB_1_0
+ if (!sanei_usb_ctx)
+ {
+ DBG (4, "%s: initializing libusb-1.0\n", __func__);
+ ret = libusb_init (&sanei_usb_ctx);
+ if (ret < 0)
+ {
+ DBG (1,
+ "%s: failed to initialize libusb-1.0, error %d\n", __func__,
+ ret);
+ return;
+ }
+#ifdef DBG_LEVEL
+ if (DBG_LEVEL > 4)
+ libusb_set_debug (sanei_usb_ctx, 3);
+#endif /* DBG_LEVEL */
+ }
+#endif /* HAVE_LIBUSB_1_0 */
+
+#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0)
+ DBG (4, "%s: SANE is built without support for libusb\n", __func__);
+#endif
+
+ /* sanei_usb is now initialized */
+ initialized++;
+
+ /* do a first scan of USB busses to fill device list */
+ sanei_usb_scan_devices();
+}
+
+void
+sanei_usb_exit (void)
+{
+int i;
+
+ /* check we have really some work to do */
+ if(initialized==0)
+ {
+ DBG (1, "%s: sanei_usb in not initialized!\n", __func__);
+ return;
+ }
+
+ /* decrement the use count */
+ initialized--;
+
+ /* if we reach 0, free allocated resources */
+ if(initialized==0)
+ {
+ /* free allocated resources */
+ DBG (4, "%s: freeing resources\n", __func__);
+ for (i = 0; i < device_number; i++)
+ {
+ if (devices[i].devname != NULL)
+ {
+ DBG (5, "%s: freeing device %02d\n", __func__, i);
+ free(devices[i].devname);
+ devices[i].devname=NULL;
+ }
+ }
+#ifdef HAVE_LIBUSB_1_0
+ if (sanei_usb_ctx)
+ {
+ libusb_exit (sanei_usb_ctx);
+ /* reset libusb-1.0 context */
+ sanei_usb_ctx=NULL;
+ }
+#endif
+ /* reset device_number */
+ device_number=0;
+ }
+ else
+ {
+ DBG (4, "%s: not freeing resources since use count is %d\n", __func__, initialized);
+ }
+ return;
+}
+
+#ifdef HAVE_USBCALLS
+/** scan for devices through usbcall method
+ * Check for devices using OS/2 USBCALLS Interface
+ */
+static void usbcall_scan_devices(void)
+{
+ SANE_Char devname[1024];
+ device_list_type device;
+ CHAR ucData[2048];
+ struct usb_device_descriptor *pDevDesc;
+ struct usb_config_descriptor *pCfgDesc;
+
+ APIRET rc;
+ ULONG ulNumDev, ulDev, ulBufLen;
+
+ ulBufLen = sizeof(ucData);
+ memset(&ucData,0,sizeof(ucData));
+ rc = UsbQueryNumberDevices( &ulNumDev);
+
+ if(rc==0 && ulNumDev)
+ {
+ for (ulDev=1; ulDev<=ulNumDev; ulDev++)
+ {
+ UsbQueryDeviceReport(ulDev, &ulBufLen, ucData);
+
+ pDevDesc = (struct usb_device_descriptor*) ucData;
+ pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor));
+ int interface=0;
+ SANE_Bool found;
+ if (!pCfgDesc->bConfigurationValue)
+ {
+ DBG (1, "%s: device 0x%04x/0x%04x is not configured\n", __func__,
+ pDevDesc->idVendor, pDevDesc->idProduct);
+ continue;
+ }
+ if (pDevDesc->idVendor == 0 || pDevDesc->idProduct == 0)
+ {
+ DBG (5, "%s: device 0x%04x/0x%04x looks like a root hub\n", __func__,
+ pDevDesc->idVendor, pDevDesc->idProduct);
+ continue;
+ }
+ found = SANE_FALSE;
+
+ if (pDevDesc->bDeviceClass == USB_CLASS_VENDOR_SPEC)
+ {
+ found = SANE_TRUE;
+ }
+
+ if (!found)
+ {
+ DBG (5, "%s: device 0x%04x/0x%04x: no suitable interfaces\n", __func__,
+ pDevDesc->idVendor, pDevDesc->idProduct);
+ continue;
+ }
+
+ snprintf (devname, sizeof (devname), "usbcalls:%d", ulDev);
+ memset (&device, 0, sizeof (device));
+ device.devname = strdup (devname);
+ device.fd = ulDev; /* store usbcalls device number */
+ device.vendor = pDevDesc->idVendor;
+ device.product = pDevDesc->idProduct;
+ device.method = sanei_usb_method_usbcalls;
+ device.interface_nr = interface;
+ DBG (4, "%s: found usbcalls device (0x%04x/0x%04x) as device number %s\n", __func__,
+ pDevDesc->idVendor, pDevDesc->idProduct,device.devname);
+ store_device(device);
+ }
+ }
+}
+#endif /* HAVE_USBCALLS */
+
+#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0)
+/** scan for devices using kernel device.
+ * Check for devices using kernel device
+ */
+static void kernel_scan_devices(void)
+{
+ SANE_String *prefix;
+ SANE_String prefixlist[] = {
+#if defined(__linux__)
+ "/dev/", "usbscanner",
+ "/dev/usb/", "scanner",
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__DragonFly__)
+ "/dev/", "uscanner",
+#elif defined(__BEOS__)
+ "/dev/scanner/usb/", "",
+#endif
+ 0, 0
+ };
+ SANE_Int vendor, product;
+ SANE_Char devname[1024];
+ int fd;
+ device_list_type device;
+
+ DBG (4, "%s: Looking for kernel scanner devices\n", __func__);
+ /* Check for devices using the kernel scanner driver */
+
+ for (prefix = prefixlist; *prefix; prefix += 2)
+ {
+ SANE_String dir_name = *prefix;
+ SANE_String base_name = *(prefix + 1);
+ struct stat stat_buf;
+ DIR *dir;
+ struct dirent *dir_entry;
+
+ if (stat (dir_name, &stat_buf) < 0)
+ {
+ DBG (5, "%s: can't stat %s: %s\n", __func__, dir_name,
+ strerror (errno));
+ continue;
+ }
+ if (!S_ISDIR (stat_buf.st_mode))
+ {
+ DBG (5, "%s: %s is not a directory\n", __func__, dir_name);
+ continue;
+ }
+ if ((dir = opendir (dir_name)) == 0)
+ {
+ DBG (5, "%s: cannot read directory %s: %s\n", __func__, dir_name,
+ strerror (errno));
+ continue;
+ }
+
+ while ((dir_entry = readdir (dir)) != 0)
+ {
+ /* skip standard dir entries */
+ if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0)
+ continue;
+
+ if (strncmp (base_name, dir_entry->d_name, strlen (base_name)) == 0)
+ {
+ if (strlen (dir_name) + strlen (dir_entry->d_name) + 1 >
+ sizeof (devname))
+ continue;
+ sprintf (devname, "%s%s", dir_name, dir_entry->d_name);
+ fd = -1;
+#ifdef HAVE_RESMGR
+ fd = rsm_open_device (devname, O_RDWR);
+#endif
+ if (fd == -1)
+ fd = open (devname, O_RDWR);
+ if (fd < 0)
+ {
+ DBG (5, "%s: couldn't open %s: %s\n", __func__, devname,
+ strerror (errno));
+ continue;
+ }
+ vendor = -1;
+ product = -1;
+ kernel_get_vendor_product (fd, devname, &vendor, &product);
+ close (fd);
+ memset (&device, 0, sizeof (device));
+ device.devname = strdup (devname);
+ if (!device.devname)
+ {
+ closedir (dir);
+ return;
+ }
+ device.vendor = vendor;
+ device.product = product;
+ device.method = sanei_usb_method_scanner_driver;
+ DBG (4,
+ "%s: found kernel scanner device (0x%04x/0x%04x) at %s\n", __func__,
+ vendor, product, devname);
+ store_device(device);
+ }
+ }
+ closedir (dir);
+ }
+}
+#endif /* !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) */
+
+#ifdef HAVE_LIBUSB
+/** scan for devices using old libusb
+ * Check for devices using 0.1.x libusb
+ */
+static void libusb_scan_devices(void)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+ SANE_Char devname[1024];
+ device_list_type device;
+
+ DBG (4, "%s: Looking for libusb devices\n", __func__);
+
+ usb_find_busses ();
+ usb_find_devices ();
+
+ /* Check for the matching device */
+ for (bus = usb_get_busses (); bus; bus = bus->next)
+ {
+ for (dev = bus->devices; dev; dev = dev->next)
+ {
+ int interface;
+ SANE_Bool found = SANE_FALSE;
+
+ if (!dev->config)
+ {
+ DBG (1,
+ "%s: device 0x%04x/0x%04x is not configured\n", __func__,
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ continue;
+ }
+ if (dev->descriptor.idVendor == 0 || dev->descriptor.idProduct == 0)
+ {
+ DBG (5,
+ "%s: device 0x%04x/0x%04x looks like a root hub\n", __func__,
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ continue;
+ }
+
+ for (interface = 0;
+ interface < dev->config[0].bNumInterfaces && !found;
+ interface++)
+ {
+ switch (dev->descriptor.bDeviceClass)
+ {
+ case USB_CLASS_VENDOR_SPEC:
+ found = SANE_TRUE;
+ break;
+ case USB_CLASS_PER_INTERFACE:
+ if (dev->config[0].interface[interface].num_altsetting == 0 ||
+ !dev->config[0].interface[interface].altsetting)
+ {
+ DBG (1, "%s: device 0x%04x/0x%04x doesn't "
+ "have an altsetting for interface %d\n", __func__,
+ dev->descriptor.idVendor, dev->descriptor.idProduct,
+ interface);
+ continue;
+ }
+ switch (dev->config[0].interface[interface].altsetting[0].
+ bInterfaceClass)
+ {
+ case USB_CLASS_VENDOR_SPEC:
+ case USB_CLASS_PER_INTERFACE:
+ case 6: /* imaging? */
+ case 16: /* data? */
+ found = SANE_TRUE;
+ break;
+ }
+ break;
+ }
+ if (!found)
+ DBG (5,
+ "%s: device 0x%04x/0x%04x, interface %d "
+ "doesn't look like a "
+ "scanner (%d/%d)\n", __func__, dev->descriptor.idVendor,
+ dev->descriptor.idProduct, interface,
+ dev->descriptor.bDeviceClass,
+ dev->config[0].interface[interface].altsetting != 0
+ ? dev->config[0].interface[interface].altsetting[0].
+ bInterfaceClass : -1);
+ }
+ interface--;
+ if (!found)
+ {
+ DBG (5,
+ "%s: device 0x%04x/0x%04x: no suitable interfaces\n", __func__,
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ continue;
+ }
+
+ memset (&device, 0, sizeof (device));
+ device.libusb_device = dev;
+ snprintf (devname, sizeof (devname), "libusb:%s:%s",
+ dev->bus->dirname, dev->filename);
+ device.devname = strdup (devname);
+ if (!device.devname)
+ return;
+ device.vendor = dev->descriptor.idVendor;
+ device.product = dev->descriptor.idProduct;
+ device.method = sanei_usb_method_libusb;
+ device.interface_nr = interface;
+ DBG (4,
+ "%s: found libusb device (0x%04x/0x%04x) interface "
+ "%d at %s\n", __func__,
+ dev->descriptor.idVendor, dev->descriptor.idProduct, interface,
+ devname);
+ store_device(device);
+ }
+ }
+}
+#endif /* HAVE_LIBUSB */
+
+#ifdef HAVE_LIBUSB_1_0
+/** scan for devices using libusb
+ * Check for devices using libusb-1.0
+ */
+static void libusb_scan_devices(void)
+{
+ device_list_type device;
+ SANE_Char devname[1024];
+ libusb_device **devlist;
+ ssize_t ndev;
+ libusb_device *dev;
+ libusb_device_handle *hdl;
+ struct libusb_device_descriptor desc;
+ struct libusb_config_descriptor *config0;
+ unsigned short vid, pid;
+ unsigned char busno, address;
+ int config;
+ int interface;
+ int ret;
+ int i;
+
+ DBG (4, "%s: Looking for libusb-1.0 devices\n", __func__);
+
+ ndev = libusb_get_device_list (sanei_usb_ctx, &devlist);
+ if (ndev < 0)
+ {
+ DBG (1,
+ "%s: failed to get libusb-1.0 device list, error %d\n", __func__,
+ (int) ndev);
+ return;
+ }
+
+ for (i = 0; i < ndev; i++)
+ {
+ SANE_Bool found = SANE_FALSE;
+
+ dev = devlist[i];
+
+ busno = libusb_get_bus_number (dev);
+ address = libusb_get_device_address (dev);
+
+ ret = libusb_get_device_descriptor (dev, &desc);
+ if (ret < 0)
+ {
+ DBG (1,
+ "%s: could not get device descriptor for device at %03d:%03d (err %d)\n", __func__,
+ busno, address, ret);
+ continue;
+ }
+
+ vid = desc.idVendor;
+ pid = desc.idProduct;
+
+ if ((vid == 0) || (pid == 0))
+ {
+ DBG (5,
+ "%s: device 0x%04x/0x%04x at %03d:%03d looks like a root hub\n", __func__,
+ vid, pid, busno, address);
+ continue;
+ }
+
+ ret = libusb_open (dev, &hdl);
+ if (ret < 0)
+ {
+ DBG (1,
+ "%s: skipping device 0x%04x/0x%04x at %03d:%03d: cannot open: %s\n", __func__,
+ vid, pid, busno, address, sanei_libusb_strerror (ret));
+
+ continue;
+ }
+
+ ret = libusb_get_configuration (hdl, &config);
+
+ libusb_close (hdl);
+
+ if (ret < 0)
+ {
+ DBG (1,
+ "%s: could not get configuration for device 0x%04x/0x%04x at %03d:%03d (err %d)\n", __func__,
+ vid, pid, busno, address, ret);
+ continue;
+ }
+
+ if (config == 0)
+ {
+ DBG (1,
+ "%s: device 0x%04x/0x%04x at %03d:%03d is not configured\n", __func__,
+ vid, pid, busno, address);
+ continue;
+ }
+
+ ret = libusb_get_config_descriptor (dev, 0, &config0);
+ if (ret < 0)
+ {
+ DBG (1,
+ "%s: could not get config[0] descriptor for device 0x%04x/0x%04x at %03d:%03d (err %d)\n", __func__,
+ vid, pid, busno, address, ret);
+ continue;
+ }
+
+ for (interface = 0; (interface < config0->bNumInterfaces) && !found; interface++)
+ {
+ switch (desc.bDeviceClass)
+ {
+ case LIBUSB_CLASS_VENDOR_SPEC:
+ found = SANE_TRUE;
+ break;
+
+ case LIBUSB_CLASS_PER_INTERFACE:
+ if ((config0->interface[interface].num_altsetting == 0)
+ || !config0->interface[interface].altsetting)
+ {
+ DBG (1, "%s: device 0x%04x/0x%04x doesn't "
+ "have an altsetting for interface %d\n", __func__,
+ vid, pid, interface);
+ continue;
+ }
+
+ switch (config0->interface[interface].altsetting[0].bInterfaceClass)
+ {
+ case LIBUSB_CLASS_VENDOR_SPEC:
+ case LIBUSB_CLASS_PER_INTERFACE:
+ case LIBUSB_CLASS_PTP:
+ case 16: /* data? */
+ found = SANE_TRUE;
+ break;
+ }
+ break;
+ }
+
+ if (!found)
+ DBG (5,
+ "%s: device 0x%04x/0x%04x, interface %d "
+ "doesn't look like a scanner (%d/%d)\n", __func__,
+ vid, pid, interface, desc.bDeviceClass,
+ (config0->interface[interface].altsetting != 0)
+ ? config0->interface[interface].altsetting[0].bInterfaceClass : -1);
+ }
+
+ libusb_free_config_descriptor (config0);
+
+ interface--;
+
+ if (!found)
+ {
+ DBG (5,
+ "%s: device 0x%04x/0x%04x at %03d:%03d: no suitable interfaces\n", __func__,
+ vid, pid, busno, address);
+ continue;
+ }
+
+ memset (&device, 0, sizeof (device));
+ device.lu_device = libusb_ref_device(dev);
+ snprintf (devname, sizeof (devname), "libusb:%03d:%03d",
+ busno, address);
+ device.devname = strdup (devname);
+ if (!device.devname)
+ return;
+ device.vendor = vid;
+ device.product = pid;
+ device.method = sanei_usb_method_libusb;
+ device.interface_nr = interface;
+ DBG (4,
+ "%s: found libusb-1.0 device (0x%04x/0x%04x) interface "
+ "%d at %s\n", __func__,
+ vid, pid, interface, devname);
+
+ store_device (device);
+ }
+
+ libusb_free_device_list (devlist, 1);
+
+}
+#endif /* HAVE_LIBUSB_1_0 */
+
+
+void
+sanei_usb_scan_devices (void)
+{
+ int count;
+ int i;
+
+ /* check USB has been initialized first */
+ if(initialized==0)
+ {
+ DBG (1, "%s: sanei_usb is not initialized!\n", __func__);
+ return;
+ }
+
+ /* we mark all already detected devices as missing */
+ /* each scan method will reset this value to 0 (not missing)
+ * when storing the device */
+ DBG (4, "%s: marking existing devices\n", __func__);
+ for (i = 0; i < device_number; i++)
+ {
+ devices[i].missing++;
+ }
+
+ /* Check for devices using the kernel scanner driver */
+#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0)
+ kernel_scan_devices();
+#endif
+
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+ /* Check for devices using libusb (old or new)*/
+ libusb_scan_devices();
+#endif
+
+#ifdef HAVE_USBCALLS
+ /* Check for devices using OS/2 USBCALLS Interface */
+ usbcall_scan_devices();
+#endif
+
+ /* display found devices */
+ if (debug_level > 5)
+ {
+ count=0;
+ for (i = 0; i < device_number; i++)
+ {
+ if(!devices[i].missing)
+ {
+ count++;
+ DBG (6, "%s: device %02d is %s\n", __func__, i, devices[i].devname);
+ }
+ }
+ DBG (5, "%s: found %d devices\n", __func__, count);
+ }
+}
+
+
+
+/* This logically belongs to sanei_config.c but not every backend that
+ uses sanei_config() wants to depend on sanei_usb. */
+void
+sanei_usb_attach_matching_devices (const char *name,
+ SANE_Status (*attach) (const char *dev))
+{
+ char *vendor, *product;
+
+ if (strncmp (name, "usb", 3) == 0)
+ {
+ SANE_Word vendorID = 0, productID = 0;
+
+ name += 3;
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &vendor);
+ if (vendor)
+ {
+ vendorID = strtol (vendor, 0, 0);
+ free (vendor);
+ }
+ name = sanei_config_skip_whitespace (name);
+ }
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &product);
+ if (product)
+ {
+ productID = strtol (product, 0, 0);
+ free (product);
+ }
+ }
+ sanei_usb_find_devices (vendorID, productID, attach);
+ }
+ else
+ (*attach) (name);
+}
+
+SANE_Status
+sanei_usb_get_vendor_product_byname (SANE_String_Const devname,
+ SANE_Word * vendor, SANE_Word * product)
+{
+ int i;
+ SANE_Bool found = SANE_FALSE;
+
+ for (i = 0; i < device_number && devices[i].devname; i++)
+ {
+ if (!devices[i].missing && strcmp (devices[i].devname, devname) == 0)
+ {
+ found = SANE_TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DBG (1, "sanei_usb_get_vendor_product_byname: can't find device `%s' in list\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ((devices[i].vendor == 0) && (devices[i].product == 0))
+ {
+ DBG (1, "sanei_usb_get_vendor_product_byname: not support for this method\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (vendor)
+ *vendor = devices[i].vendor;
+
+ if (product)
+ *product = devices[i].product;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_get_vendor_product (SANE_Int dn, SANE_Word * vendor,
+ SANE_Word * product)
+{
+ SANE_Word vendorID = 0;
+ SANE_Word productID = 0;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_get_vendor_product: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (devices[dn].missing >= 1)
+ {
+ DBG (1, "sanei_usb_get_vendor_product: dn=%d is missing!\n",dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* kernel, usbcal and libusb methods store these when device scanning
+ * is done, so we can use them directly */
+ vendorID = devices[dn].vendor;
+ productID = devices[dn].product;
+
+ if (vendor)
+ *vendor = vendorID;
+ if (product)
+ *product = productID;
+
+ if (!vendorID || !productID)
+ {
+ DBG (3, "sanei_usb_get_vendor_product: device %d: Your OS doesn't "
+ "seem to support detection of vendor+product ids\n", dn);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ else
+ {
+ DBG (3, "sanei_usb_get_vendor_product: device %d: vendorID: 0x%04x, "
+ "productID: 0x%04x\n", dn, vendorID, productID);
+ return SANE_STATUS_GOOD;
+ }
+}
+
+SANE_Status
+sanei_usb_find_devices (SANE_Int vendor, SANE_Int product,
+ SANE_Status (*attach) (SANE_String_Const dev))
+{
+ SANE_Int dn = 0;
+
+ DBG (3,
+ "sanei_usb_find_devices: vendor=0x%04x, product=0x%04x\n",
+ vendor, product);
+
+ while (devices[dn].devname && dn < device_number)
+ {
+ if (devices[dn].vendor == vendor
+ && devices[dn].product == product
+ && !devices[dn].missing
+ && attach)
+ attach (devices[dn].devname);
+ dn++;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_usb_set_endpoint (SANE_Int dn, SANE_Int ep_type, SANE_Int ep)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_set_endpoint: dn >= device number || dn < 0\n");
+ return;
+ }
+
+ DBG (5, "sanei_usb_set_endpoint: Setting endpoint of type 0x%02x to 0x%02x\n", ep_type, ep);
+ switch (ep_type)
+ {
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_BULK:
+ devices[dn].bulk_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK:
+ devices[dn].bulk_out_ep = ep;
+ break;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ devices[dn].iso_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ devices[dn].iso_out_ep = ep;
+ break;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_INTERRUPT:
+ devices[dn].int_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_INTERRUPT:
+ devices[dn].int_out_ep = ep;
+ break;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_CONTROL:
+ devices[dn].control_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_CONTROL:
+ devices[dn].control_out_ep = ep;
+ break;
+ }
+}
+
+SANE_Int
+sanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_get_endpoint: dn >= device number || dn < 0\n");
+ return 0;
+ }
+
+ switch (ep_type)
+ {
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_BULK:
+ return devices[dn].bulk_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK:
+ return devices[dn].bulk_out_ep;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ return devices[dn].iso_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ return devices[dn].iso_out_ep;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_INTERRUPT:
+ return devices[dn].int_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_INTERRUPT:
+ return devices[dn].int_out_ep;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_CONTROL:
+ return devices[dn].control_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_CONTROL:
+ return devices[dn].control_out_ep;
+ default:
+ return 0;
+ }
+}
+
+SANE_Status
+sanei_usb_open (SANE_String_Const devname, SANE_Int * dn)
+{
+ int devcount;
+ SANE_Bool found = SANE_FALSE;
+ int c, i, a;
+
+ DBG (5, "sanei_usb_open: trying to open device `%s'\n", devname);
+ if (!dn)
+ {
+ DBG (1, "sanei_usb_open: can't open `%s': dn == NULL\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (devcount = 0;
+ devcount < device_number && devices[devcount].devname != 0;
+ devcount++)
+ {
+ if (!devices[devcount].missing && strcmp (devices[devcount].devname, devname) == 0)
+ {
+ if (devices[devcount].open)
+ {
+ DBG (1, "sanei_usb_open: device `%s' already open\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ found = SANE_TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DBG (1, "sanei_usb_open: can't find device `%s' in list\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (devices[devcount].method == sanei_usb_method_libusb)
+ {
+#ifdef HAVE_LIBUSB
+ struct usb_device *dev;
+ struct usb_interface_descriptor *interface;
+ int result, num;
+
+ devices[devcount].libusb_handle =
+ usb_open (devices[devcount].libusb_device);
+ if (!devices[devcount].libusb_handle)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
+ devname, strerror (errno));
+ if (errno == EPERM || errno == EACCES)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (errno == EBUSY)
+ {
+ DBG (1, "Maybe the kernel scanner driver claims the "
+ "scanner's interface?\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ return status;
+ }
+
+ dev = usb_device (devices[devcount].libusb_handle);
+
+ /* Set the configuration */
+ if (!dev->config)
+ {
+ DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ if (dev->descriptor.bNumConfigurations > 1)
+ {
+ DBG (3, "sanei_usb_open: more than one "
+ "configuration (%d), choosing first config (%d)\n",
+ dev->descriptor.bNumConfigurations,
+ dev->config[0].bConfigurationValue);
+ }
+ result = usb_set_configuration (devices[devcount].libusb_handle,
+ dev->config[0].bConfigurationValue);
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: libusb complained: %s\n", usb_strerror ());
+ if (errno == EPERM || errno == EACCES)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (errno == EBUSY)
+ {
+ DBG (3, "Maybe the kernel scanner driver or usblp claims the "
+ "interface? Ignoring this error...\n");
+ status = SANE_STATUS_GOOD;
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ usb_close (devices[devcount].libusb_handle);
+ return status;
+ }
+ }
+
+ /* Claim the interface */
+ result = usb_claim_interface (devices[devcount].libusb_handle,
+ devices[devcount].interface_nr);
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: libusb complained: %s\n", usb_strerror ());
+ if (errno == EPERM || errno == EACCES)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (errno == EBUSY)
+ {
+ DBG (1, "Maybe the kernel scanner driver claims the "
+ "scanner's interface?\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ usb_close (devices[devcount].libusb_handle);
+ return status;
+ }
+
+ /* Loop through all of the configurations */
+ for (c = 0; c < dev->descriptor.bNumConfigurations; c++)
+ {
+ /* Loop through all of the interfaces */
+ for (i = 0; i < dev->config[c].bNumInterfaces; i++)
+ {
+ /* Loop through all of the alternate settings */
+ for (a = 0; a < dev->config[c].interface[i].num_altsetting; a++)
+ {
+ DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
+ DBG (5, "sanei_usb_open: interface nr: %d\n", i);
+ DBG (5, "sanei_usb_open: alt_setting nr: %d\n", a);
+
+ /* Start by interfaces found in sanei_usb_init */
+ if (c == 0 && i != devices[devcount].interface_nr)
+ {
+ DBG (5, "sanei_usb_open: interface %d not detected as "
+ "a scanner by sanei_usb_init, ignoring.\n", i);
+ continue;
+ }
+
+ interface = &dev->config[c].interface[i].altsetting[a];
+
+ /* Now we look for usable endpoints */
+ for (num = 0; num < interface->bNumEndpoints; num++)
+ {
+ struct usb_endpoint_descriptor *endpoint;
+ int address, direction, transfer_type;
+
+ endpoint = &interface->endpoint[num];
+ DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
+ transfer_type =
+ endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+ address =
+ endpoint->
+ bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK;
+ direction =
+ endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+
+ DBG (5, "sanei_usb_open: direction: %d\n", direction);
+
+ DBG (5,
+ "sanei_usb_open: address: %d transfertype: %d\n",
+ address, transfer_type);
+
+
+ /* save the endpoints we need later */
+ if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT)
+ {
+ DBG (5,
+ "sanei_usb_open: found interrupt-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].int_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_in_ep);
+ else
+ devices[devcount].int_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].int_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_out_ep);
+ else
+ devices[devcount].int_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_BULK)
+ {
+ DBG (5,
+ "sanei_usb_open: found bulk-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].bulk_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a bulk-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_in_ep);
+ else
+ devices[devcount].bulk_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].bulk_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a bulk-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_out_ep);
+ else
+ devices[devcount].bulk_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS)
+ {
+ DBG (5,
+ "sanei_usb_open: found isochronous-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].iso_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_in_ep);
+ else
+ devices[devcount].iso_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].iso_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_out_ep);
+ else
+ devices[devcount].iso_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_CONTROL)
+ {
+ DBG (5,
+ "sanei_usb_open: found control-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].control_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_in_ep);
+ else
+ devices[devcount].control_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].control_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_out_ep);
+ else
+ devices[devcount].control_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ }
+ }
+ }
+ }
+
+#elif defined(HAVE_LIBUSB_1_0) /* libusb-1.0 */
+
+ int config;
+ libusb_device *dev;
+ struct libusb_device_descriptor desc;
+ struct libusb_config_descriptor *config0;
+ int result, num;
+
+ dev = devices[devcount].lu_device;
+
+ result = libusb_open (dev, &devices[devcount].lu_handle);
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
+ devname, sanei_libusb_strerror (result));
+ if (result == LIBUSB_ERROR_ACCESS)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (result == LIBUSB_ERROR_BUSY)
+ {
+ DBG (1, "Maybe the kernel scanner driver claims the "
+ "scanner's interface?\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (result == LIBUSB_ERROR_NO_MEM)
+ {
+ status = SANE_STATUS_NO_MEM;
+ }
+ return status;
+ }
+
+ result = libusb_get_configuration (devices[devcount].lu_handle, &config);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get configuration for device `%s' (err %d)\n",
+ devname, result);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (config == 0)
+ {
+ DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ result = libusb_get_device_descriptor (dev, &desc);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get device descriptor for device `%s' (err %d)\n",
+ devname, result);
+ return SANE_STATUS_INVAL;
+ }
+
+ result = libusb_get_config_descriptor (dev, 0, &config0);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get config[0] descriptor for device `%s' (err %d)\n",
+ devname, result);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Set the configuration */
+ if (desc.bNumConfigurations > 1)
+ {
+ DBG (3, "sanei_usb_open: more than one "
+ "configuration (%d), choosing first config (%d)\n",
+ desc.bNumConfigurations,
+ config0->bConfigurationValue);
+ }
+ result = libusb_set_configuration (devices[devcount].lu_handle,
+ config0->bConfigurationValue);
+
+ libusb_free_config_descriptor (config0);
+
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ if (result == LIBUSB_ERROR_ACCESS)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (result == LIBUSB_ERROR_BUSY)
+ {
+ DBG (3, "Maybe the kernel scanner driver or usblp claims the "
+ "interface? Ignoring this error...\n");
+ status = SANE_STATUS_GOOD;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ libusb_close (devices[devcount].lu_handle);
+ return status;
+ }
+ }
+
+ /* Claim the interface */
+ result = libusb_claim_interface (devices[devcount].lu_handle,
+ devices[devcount].interface_nr);
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ if (result == LIBUSB_ERROR_ACCESS)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (result == LIBUSB_ERROR_BUSY)
+ {
+ DBG (1, "Maybe the kernel scanner driver claims the "
+ "scanner's interface?\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ libusb_close (devices[devcount].lu_handle);
+ return status;
+ }
+
+ /* Loop through all of the configurations */
+ for (c = 0; c < desc.bNumConfigurations; c++)
+ {
+ struct libusb_config_descriptor *config;
+
+ result = libusb_get_config_descriptor (dev, c, &config);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get config[%d] descriptor for device `%s' (err %d)\n",
+ c, devname, result);
+ continue;
+ }
+
+ /* Loop through all of the interfaces */
+ for (i = 0; i < config->bNumInterfaces; i++)
+ {
+ /* Loop through all of the alternate settings */
+ for (a = 0; a < config->interface[i].num_altsetting; a++)
+ {
+ const struct libusb_interface_descriptor *interface;
+
+ DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
+ DBG (5, "sanei_usb_open: interface nr: %d\n", i);
+ DBG (5, "sanei_usb_open: alt_setting nr: %d\n", a);
+
+ /* Start by interfaces found in sanei_usb_init */
+ if (c == 0 && i != devices[devcount].interface_nr)
+ {
+ DBG (5, "sanei_usb_open: interface %d not detected as "
+ "a scanner by sanei_usb_init, ignoring.\n", i);
+ continue;
+ }
+
+ interface = &config->interface[i].altsetting[a];
+
+ /* Now we look for usable endpoints */
+ for (num = 0; num < interface->bNumEndpoints; num++)
+ {
+ const struct libusb_endpoint_descriptor *endpoint;
+ int address, direction, transfer_type;
+
+ endpoint = &interface->endpoint[num];
+ DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
+
+ transfer_type = endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
+ address = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK;
+ direction = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK;
+
+ DBG (5, "sanei_usb_open: direction: %d\n", direction);
+ DBG (5, "sanei_usb_open: address: %d transfertype: %d\n",
+ address, transfer_type);
+
+ /* save the endpoints we need later */
+ if (transfer_type == LIBUSB_TRANSFER_TYPE_INTERRUPT)
+ {
+ DBG (5,
+ "sanei_usb_open: found interrupt-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].int_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_in_ep);
+ else
+ devices[devcount].int_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].int_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_out_ep);
+ else
+ devices[devcount].int_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == LIBUSB_TRANSFER_TYPE_BULK)
+ {
+ DBG (5,
+ "sanei_usb_open: found bulk-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].bulk_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a bulk-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_in_ep);
+ else
+ devices[devcount].bulk_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].bulk_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a bulk-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_out_ep);
+ else
+ devices[devcount].bulk_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ {
+ DBG (5,
+ "sanei_usb_open: found isochronous-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].iso_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_in_ep);
+ else
+ devices[devcount].iso_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].iso_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_out_ep);
+ else
+ devices[devcount].iso_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == LIBUSB_TRANSFER_TYPE_CONTROL)
+ {
+ DBG (5,
+ "sanei_usb_open: found control-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].control_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_in_ep);
+ else
+ devices[devcount].control_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].control_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_out_ep);
+ else
+ devices[devcount].control_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ }
+ }
+ }
+
+ libusb_free_config_descriptor (config);
+ }
+
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_open: can't open device `%s': "
+ "libusb support missing\n", devname);
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ }
+ else if (devices[devcount].method == sanei_usb_method_scanner_driver)
+ {
+#ifdef FD_CLOEXEC
+ long int flag;
+#endif
+ /* Using kernel scanner driver */
+ devices[devcount].fd = -1;
+#ifdef HAVE_RESMGR
+ devices[devcount].fd = rsm_open_device (devname, O_RDWR);
+#endif
+ if (devices[devcount].fd == -1)
+ devices[devcount].fd = open (devname, O_RDWR);
+ if (devices[devcount].fd < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ if (errno == EACCES)
+ status = SANE_STATUS_ACCESS_DENIED;
+ else if (errno == ENOENT)
+ {
+ DBG (5, "sanei_usb_open: open of `%s' failed: %s\n",
+ devname, strerror (errno));
+ return status;
+ }
+ DBG (1, "sanei_usb_open: open of `%s' failed: %s\n",
+ devname, strerror (errno));
+ return status;
+ }
+#ifdef FD_CLOEXEC
+ flag = fcntl (devices[devcount].fd, F_GETFD);
+ if (flag >= 0)
+ {
+ if (fcntl (devices[devcount].fd, F_SETFD, flag | FD_CLOEXEC) < 0)
+ DBG (1, "sanei_usb_open: fcntl of `%s' failed: %s\n",
+ devname, strerror (errno));
+ }
+#endif
+ }
+ else if (devices[devcount].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ CHAR ucData[2048];
+ struct usb_device_descriptor *pDevDesc;
+ struct usb_config_descriptor *pCfgDesc;
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_descriptor_header *pDescHead;
+
+ ULONG ulBufLen;
+ ulBufLen = sizeof(ucData);
+ memset(&ucData,0,sizeof(ucData));
+
+ int result, rc;
+ int address, direction, transfer_type;
+
+ DBG (5, "devname = %s, devcount = %d\n",devices[devcount].devname,devcount);
+ DBG (5, "USBCalls device number to open = %d\n",devices[devcount].fd);
+ DBG (5, "USBCalls Vendor/Product to open = 0x%04x/0x%04x\n",
+ devices[devcount].vendor,devices[devcount].product);
+
+ rc = UsbOpen (&dh,
+ devices[devcount].vendor,
+ devices[devcount].product,
+ USB_ANY_PRODUCTVERSION,
+ USB_OPEN_FIRST_UNUSED);
+ DBG (1, "sanei_usb_open: UsbOpen rc = %d\n",rc);
+ if (rc!=0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+ DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
+ devname, strerror (rc));
+ return status;
+ }
+ rc = UsbQueryDeviceReport( devices[devcount].fd,
+ &ulBufLen,
+ ucData);
+ DBG (1, "sanei_usb_open: UsbQueryDeviceReport rc = %d\n",rc);
+ pDevDesc = (struct usb_device_descriptor*)ucData;
+ pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor));
+ UCHAR *pCurPtr = (UCHAR*) pCfgDesc;
+ UCHAR *pEndPtr = pCurPtr+ pCfgDesc->wTotalLength;
+ pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength);
+ /* Set the configuration */
+ if (pDevDesc->bNumConfigurations > 1)
+ {
+ DBG (3, "sanei_usb_open: more than one "
+ "configuration (%d), choosing first config (%d)\n",
+ pDevDesc->bNumConfigurations,
+ pCfgDesc->bConfigurationValue);
+ }
+ DBG (5, "UsbDeviceSetConfiguration parameters: dh = %p, bConfigurationValue = %d\n",
+ dh,pCfgDesc->bConfigurationValue);
+ result = UsbDeviceSetConfiguration (dh,
+ pCfgDesc->bConfigurationValue);
+ DBG (1, "sanei_usb_open: UsbDeviceSetConfiguration rc = %d\n",result);
+ if (result)
+ {
+ DBG (1, "sanei_usb_open: usbcalls complained on UsbDeviceSetConfiguration, rc= %d\n", result);
+ UsbClose (dh);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+
+ /* Now we look for usable endpoints */
+
+ for (pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength);
+ pDescHead;pDescHead = GetNextDescriptor(pDescHead,pEndPtr) )
+ {
+ switch(pDescHead->bDescriptorType)
+ {
+ case USB_DT_INTERFACE:
+ interface = (struct usb_interface_descriptor *) pDescHead;
+ DBG (5, "Found %d endpoints\n",interface->bNumEndpoints);
+ DBG (5, "bAlternateSetting = %d\n",interface->bAlternateSetting);
+ break;
+ case USB_DT_ENDPOINT:
+ endpoint = (struct usb_endpoint_descriptor*)pDescHead;
+ address = endpoint->bEndpointAddress;
+ direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+ /* save the endpoints we need later */
+ if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT)
+ {
+ DBG (5, "sanei_usb_open: found interupt-%s endpoint (address %2x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].int_in_ep)
+ DBG (3, "sanei_usb_open: we already have a int-in endpoint "
+ "(address: %d), ignoring the new one\n",
+ devices[devcount].int_in_ep);
+ else
+ devices[devcount].int_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ if (devices[devcount].int_out_ep)
+ DBG (3, "sanei_usb_open: we already have a int-out endpoint "
+ "(address: %d), ignoring the new one\n",
+ devices[devcount].int_out_ep);
+ else
+ devices[devcount].int_out_ep = endpoint->bEndpointAddress;
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_BULK)
+ {
+ DBG (5, "sanei_usb_open: found bulk-%s endpoint (address %2x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].bulk_in_ep)
+ DBG (3, "sanei_usb_open: we already have a bulk-in endpoint "
+ "(address: %d), ignoring the new one\n",
+ devices[devcount].bulk_in_ep);
+ else
+ devices[devcount].bulk_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].bulk_out_ep)
+ DBG (3, "sanei_usb_open: we already have a bulk-out endpoint "
+ "(address: %d), ignoring the new one\n",
+ devices[devcount].bulk_out_ep);
+ else
+ devices[devcount].bulk_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ /* ignore currently unsupported endpoints */
+ else {
+ DBG (5, "sanei_usb_open: ignoring %s-%s endpoint "
+ "(address: %d)\n",
+ transfer_type == USB_ENDPOINT_TYPE_CONTROL ? "control" :
+ transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS
+ ? "isochronous" : "interrupt",
+ direction ? "in" : "out", address);
+ continue;
+ }
+ break;
+ }
+ }
+#else
+ DBG (1, "sanei_usb_open: can't open device `%s': "
+ "usbcalls support missing\n", devname);
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_open: access method %d not implemented\n",
+ devices[devcount].method);
+ return SANE_STATUS_INVAL;
+ }
+
+ devices[devcount].open = SANE_TRUE;
+ *dn = devcount;
+ DBG (3, "sanei_usb_open: opened usb device `%s' (*dn=%d)\n",
+ devname, devcount);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_usb_close (SANE_Int dn)
+{
+ DBG (5, "sanei_usb_close: closing device %d\n", dn);
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_close: dn >= device number || dn < 0\n");
+ return;
+ }
+ if (!devices[dn].open)
+ {
+ DBG (1, "sanei_usb_close: device %d already closed or never opened\n",
+ dn);
+ return;
+ }
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ close (devices[dn].fd);
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ rc=UsbClose (dh);
+ DBG (5,"rc of UsbClose = %d\n",rc);
+#else
+ DBG (1, "sanei_usb_close: usbcalls support missing\n");
+#endif
+ }
+ else
+#ifdef HAVE_LIBUSB
+ {
+#if 0
+ /* Should only be done in case of a stall */
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].iso_in_ep);
+ /* be careful, we don't know if we are in DATA0 stage now */
+ usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ usb_resetep (devices[dn].libusb_handle, devices[dn].iso_in_ep);
+#endif /* 0 */
+ usb_release_interface (devices[dn].libusb_handle,
+ devices[dn].interface_nr);
+ usb_close (devices[dn].libusb_handle);
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ libusb_release_interface (devices[dn].lu_handle,
+ devices[dn].interface_nr);
+ libusb_close (devices[dn].lu_handle);
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_close: libusb support missing\n");
+#endif
+ devices[dn].open = SANE_FALSE;
+ return;
+}
+
+void
+sanei_usb_set_timeout (SANE_Int timeout)
+{
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+ libusb_timeout = timeout;
+#else
+ DBG (1, "sanei_usb_set_timeout: libusb support missing\n");
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+}
+
+SANE_Status
+sanei_usb_clear_halt (SANE_Int dn)
+{
+#ifdef HAVE_LIBUSB
+ int ret;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_clear_halt: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* be careful, we don't know if we are in DATA0 stage now
+ ret = usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ ret = usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ */
+#elif defined(HAVE_LIBUSB_1_0)
+ int ret;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_clear_halt: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_clear_halt: libusb support missing\n");
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_reset (SANE_Int dn)
+{
+#ifdef HAVE_LIBUSB
+ int ret;
+
+ ret = usb_reset (devices[dn].libusb_handle);
+ if (ret){
+ DBG (1, "sanei_usb_reset: ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+#elif defined(HAVE_LIBUSB_1_0)
+ int ret;
+
+ ret = libusb_reset_device (devices[dn].lu_handle);
+ if (ret){
+ DBG (1, "sanei_usb_reset: ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_reset: libusb support missing\n");
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size)
+{
+ ssize_t read_size = 0;
+
+ if (!size)
+ {
+ DBG (1, "sanei_usb_read_bulk: size == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_read_bulk: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "sanei_usb_read_bulk: trying to read %lu bytes\n",
+ (unsigned long) *size);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+ read_size = read (devices[dn].fd, buffer, *size);
+
+ if (read_size < 0)
+ DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
+ strerror (errno));
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ if (devices[dn].bulk_in_ep)
+ {
+ read_size = usb_bulk_read (devices[dn].libusb_handle,
+ devices[dn].bulk_in_ep, (char *) buffer,
+ (int) *size, libusb_timeout);
+
+ if (read_size < 0)
+ DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
+ strerror (errno));
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ if (devices[dn].bulk_in_ep)
+ {
+ int ret;
+ ret = libusb_bulk_transfer (devices[dn].lu_handle,
+ devices[dn].bulk_in_ep, buffer,
+ (int) *size, (int *) &read_size,
+ libusb_timeout);
+
+ if (ret < 0)
+ {
+ DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
+ sanei_libusb_strerror (ret));
+
+ read_size = -1;
+ }
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_read_bulk: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ char* buffer_ptr = (char*) buffer;
+ while (*size)
+ {
+ ULONG ulToRead = (*size>MAX_RW)?MAX_RW:*size;
+ ULONG ulNum = ulToRead;
+ DBG (5, "Entered usbcalls UsbBulkRead with dn = %d\n",dn);
+ DBG (5, "Entered usbcalls UsbBulkRead with dh = %p\n",dh);
+ DBG (5, "Entered usbcalls UsbBulkRead with bulk_in_ep = 0x%02x\n",devices[dn].bulk_in_ep);
+ DBG (5, "Entered usbcalls UsbBulkRead with interface_nr = %d\n",devices[dn].interface_nr);
+ DBG (5, "Entered usbcalls UsbBulkRead with usbcalls_timeout = %d\n",usbcalls_timeout);
+
+ if (devices[dn].bulk_in_ep){
+ rc = UsbBulkRead (dh, devices[dn].bulk_in_ep, devices[dn].interface_nr,
+ &ulToRead, buffer_ptr, usbcalls_timeout);
+ DBG (1, "sanei_usb_read_bulk: rc = %d\n",rc);}
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (rc || (ulNum!=ulToRead)) return SANE_STATUS_INVAL;
+ *size -=ulToRead;
+ buffer_ptr += ulToRead;
+ read_size += ulToRead;
+ }
+#else /* not HAVE_USBCALLS */
+ {
+ DBG (1, "sanei_usb_read_bulk: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (read_size < 0)
+ {
+#ifdef HAVE_LIBUSB
+ if (devices[dn].method == sanei_usb_method_libusb)
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+#elif defined(HAVE_LIBUSB_1_0)
+ if (devices[dn].method == sanei_usb_method_libusb)
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
+#endif
+ *size = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (read_size == 0)
+ {
+ DBG (3, "sanei_usb_read_bulk: read returned EOF\n");
+ *size = 0;
+ return SANE_STATUS_EOF;
+ }
+ if (debug_level > 10)
+ print_buffer (buffer, read_size);
+ DBG (5, "sanei_usb_read_bulk: wanted %lu bytes, got %ld bytes\n",
+ (unsigned long) *size, (unsigned long) read_size);
+ *size = read_size;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size)
+{
+ ssize_t write_size = 0;
+
+ if (!size)
+ {
+ DBG (1, "sanei_usb_write_bulk: size == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_write_bulk: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "sanei_usb_write_bulk: trying to write %lu bytes\n",
+ (unsigned long) *size);
+ if (debug_level > 10)
+ print_buffer (buffer, *size);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+ write_size = write (devices[dn].fd, buffer, *size);
+
+ if (write_size < 0)
+ DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
+ strerror (errno));
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ if (devices[dn].bulk_out_ep)
+ {
+ write_size = usb_bulk_write (devices[dn].libusb_handle,
+ devices[dn].bulk_out_ep,
+ (const char *) buffer,
+ (int) *size, libusb_timeout);
+ if (write_size < 0)
+ DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
+ strerror (errno));
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ if (devices[dn].bulk_out_ep)
+ {
+ int ret;
+ int trans_bytes;
+ ret = libusb_bulk_transfer (devices[dn].lu_handle,
+ devices[dn].bulk_out_ep,
+ buffer,
+ (int) *size, &trans_bytes,
+ libusb_timeout);
+ if (ret < 0)
+ {
+ DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
+ sanei_libusb_strerror (ret));
+
+ write_size = -1;
+ }
+ else
+ write_size = trans_bytes;
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_write_bulk: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ DBG (5, "Entered usbcalls UsbBulkWrite with dn = %d\n",dn);
+ DBG (5, "Entered usbcalls UsbBulkWrite with dh = %p\n",dh);
+ DBG (5, "Entered usbcalls UsbBulkWrite with bulk_out_ep = 0x%02x\n",devices[dn].bulk_out_ep);
+ DBG (5, "Entered usbcalls UsbBulkWrite with interface_nr = %d\n",devices[dn].interface_nr);
+ DBG (5, "Entered usbcalls UsbBulkWrite with usbcalls_timeout = %d\n",usbcalls_timeout);
+ while (*size)
+ {
+ ULONG ulToWrite = (*size>MAX_RW)?MAX_RW:*size;
+
+ DBG (5, "size requested to write = %lu, ulToWrite = %lu\n",(unsigned long) *size,ulToWrite);
+ if (devices[dn].bulk_out_ep){
+ rc = UsbBulkWrite (dh, devices[dn].bulk_out_ep, devices[dn].interface_nr,
+ ulToWrite, (char*) buffer, usbcalls_timeout);
+ DBG (1, "sanei_usb_write_bulk: rc = %d\n",rc);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: can't read without a bulk-out endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (rc) return SANE_STATUS_INVAL;
+ *size -=ulToWrite;
+ buffer += ulToWrite;
+ write_size += ulToWrite;
+ DBG (5, "size = %d, write_size = %d\n",*size, write_size);
+ }
+#else /* not HAVE_USBCALLS */
+ {
+ DBG (1, "sanei_usb_write_bulk: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (write_size < 0)
+ {
+ *size = 0;
+#ifdef HAVE_LIBUSB
+ if (devices[dn].method == sanei_usb_method_libusb)
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+#elif defined(HAVE_LIBUSB_1_0)
+ if (devices[dn].method == sanei_usb_method_libusb)
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
+#endif
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "sanei_usb_write_bulk: wanted %lu bytes, wrote %ld bytes\n",
+ (unsigned long) *size, (unsigned long) write_size);
+ *size = write_size;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req,
+ SANE_Int value, SANE_Int index, SANE_Int len,
+ SANE_Byte * data)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_control_msg: rtype = 0x%02x, req = %d, value = %d, "
+ "index = %d, len = %d\n", rtype, req, value, index, len);
+ if (!(rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ struct ctrlmsg_ioctl c;
+
+ c.req.requesttype = rtype;
+ c.req.request = req;
+ c.req.value = value;
+ c.req.index = index;
+ c.req.length = len;
+ c.data = data;
+
+ if (ioctl (devices[dn].fd, SCANNER_IOCTL_CTRLMSG, &c) < 0)
+ {
+ DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+#elif defined(__BEOS__)
+ struct usb_scanner_ioctl_ctrlmsg c;
+
+ c.req.request_type = rtype;
+ c.req.request = req;
+ c.req.value = value;
+ c.req.index = index;
+ c.req.length = len;
+ c.data = data;
+
+ if (ioctl (devices[dn].fd, B_SCANNER_IOCTL_CTRLMSG, &c) < 0)
+ {
+ DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_control_msg: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_control_msg (devices[dn].libusb_handle, rtype, req,
+ value, index, (char *) data, len,
+ libusb_timeout);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_control_transfer (devices[dn].lu_handle, rtype, req,
+ value, index, data, len,
+ libusb_timeout);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0*/
+ {
+ DBG (1, "sanei_usb_control_msg: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int result;
+
+ result = UsbCtrlMessage (dh, rtype, req,
+ value, index, len, (char *) data,
+ usbcalls_timeout);
+ DBG (5, "rc of usb_control_msg = %d\n",result);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: usbcalls complained: %d\n",result);
+ return SANE_STATUS_INVAL;
+ }
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+#else /* not HAVE_USBCALLS */
+ {
+ DBG (1, "sanei_usb_control_msg: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_control_msg: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
+{
+ ssize_t read_size = 0;
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+ SANE_Bool stalled = SANE_FALSE;
+#endif
+
+ if (!size)
+ {
+ DBG (1, "sanei_usb_read_int: size == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_read_int: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_read_int: trying to read %lu bytes\n",
+ (unsigned long) *size);
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+ DBG (1, "sanei_usb_read_int: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ if (devices[dn].int_in_ep)
+ {
+ read_size = usb_interrupt_read (devices[dn].libusb_handle,
+ devices[dn].int_in_ep,
+ (char *) buffer, (int) *size,
+ libusb_timeout);
+
+ if (read_size < 0)
+ DBG (1, "sanei_usb_read_int: read failed: %s\n",
+ strerror (errno));
+
+ stalled = (read_size == -EPIPE);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: can't read without an int "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ if (devices[dn].int_in_ep)
+ {
+ int ret;
+ int trans_bytes;
+ ret = libusb_interrupt_transfer (devices[dn].lu_handle,
+ devices[dn].int_in_ep,
+ buffer, (int) *size,
+ &trans_bytes, libusb_timeout);
+
+ if (ret < 0)
+ read_size = -1;
+ else
+ read_size = trans_bytes;
+
+ stalled = (ret == LIBUSB_ERROR_PIPE);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: can't read without an int "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_read_int: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ USHORT usNumBytes=*size;
+ DBG (5, "Entered usbcalls UsbIrqStart with dn = %d\n",dn);
+ DBG (5, "Entered usbcalls UsbIrqStart with dh = %p\n",dh);
+ DBG (5, "Entered usbcalls UsbIrqStart with int_in_ep = 0x%02x\n",devices[dn].int_in_ep);
+ DBG (5, "Entered usbcalls UsbIrqStart with interface_nr = %d\n",devices[dn].interface_nr);
+ DBG (5, "Entered usbcalls UsbIrqStart with bytes to read = %u\n",usNumBytes);
+
+ if (devices[dn].int_in_ep){
+ rc = UsbIrqStart (dh,devices[dn].int_in_ep,devices[dn].interface_nr,
+ usNumBytes, (char *) buffer, pUsbIrqStartHev);
+ DBG (5, "rc of UsbIrqStart = %d\n",rc);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: can't read without an int "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (rc) return SANE_STATUS_INVAL;
+ read_size += usNumBytes;
+#else
+ DBG (1, "sanei_usb_read_int: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (read_size < 0)
+ {
+#ifdef HAVE_LIBUSB
+ if (devices[dn].method == sanei_usb_method_libusb)
+ if (stalled)
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].int_in_ep);
+#elif defined(HAVE_LIBUSB_1_0)
+ if (devices[dn].method == sanei_usb_method_libusb)
+ if (stalled)
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].int_in_ep);
+#endif
+ *size = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (read_size == 0)
+ {
+ DBG (3, "sanei_usb_read_int: read returned EOF\n");
+ *size = 0;
+ return SANE_STATUS_EOF;
+ }
+ DBG (5, "sanei_usb_read_int: wanted %lu bytes, got %ld bytes\n",
+ (unsigned long) *size, (unsigned long) read_size);
+ *size = read_size;
+ if (debug_level > 10)
+ print_buffer (buffer, read_size);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_set_configuration: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_set_configuration: configuration = %d\n", configuration);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_set_configuration: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result =
+ usb_set_configuration (devices[dn].libusb_handle, configuration);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_set_configuration (devices[dn].lu_handle, configuration);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_set_configuration: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1,
+ "sanei_usb_set_configuration: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_claim_interface: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+ if (devices[dn].missing)
+ {
+ DBG (1, "sanei_usb_claim_interface: device dn=%d is missing\n", dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_claim_interface: interface_number = %d\n", interface_number);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_claim_interface: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_claim_interface (devices[dn].libusb_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_claim_interface (devices[dn].lu_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_claim_interface: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1, "sanei_usb_claim_interface: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_release_interface: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+ if (devices[dn].missing)
+ {
+ DBG (1, "sanei_usb_release_interface: device dn=%d is missing\n", dn);
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "sanei_usb_release_interface: interface_number = %d\n", interface_number);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_release_interface: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_release_interface (devices[dn].libusb_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_release_interface (devices[dn].lu_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_release_interface: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1,
+ "sanei_usb_release_interface: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_set_altinterface: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_set_altinterface: alternate = %d\n", alternate);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_set_altinterface: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_set_altinterface (devices[dn].libusb_handle, alternate);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_altinterface: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_set_interface_alt_setting (devices[dn].lu_handle,
+ devices[dn].interface_nr, alternate);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_altinterface: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_set_altinterface: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1,
+ "sanei_usb_set_altinterface: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+extern SANE_Status
+sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc )
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_get_descriptor: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_get_descriptor\n");
+#ifdef HAVE_LIBUSB
+ {
+ struct usb_device_descriptor *usb_descr;
+
+ usb_descr = &(devices[dn].libusb_device->descriptor);
+ desc->desc_type = usb_descr->bDescriptorType;
+ desc->bcd_usb = usb_descr->bcdUSB;
+ desc->bcd_dev = usb_descr->bcdDevice;
+ desc->dev_class = usb_descr->bDeviceClass;
+
+ desc->dev_sub_class = usb_descr->bDeviceSubClass;
+ desc->dev_protocol = usb_descr->bDeviceProtocol;
+ desc->max_packet_size = usb_descr->bMaxPacketSize0;
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ struct libusb_device_descriptor lu_desc;
+ int ret;
+
+ ret = libusb_get_device_descriptor (devices[dn].lu_device, &lu_desc);
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_get_descriptor: libusb error: %s\n",
+ sanei_libusb_strerror (ret));
+
+ return SANE_STATUS_INVAL;
+ }
+
+ desc->desc_type = lu_desc.bDescriptorType;
+ desc->bcd_usb = lu_desc.bcdUSB;
+ desc->bcd_dev = lu_desc.bcdDevice;
+ desc->dev_class = lu_desc.bDeviceClass;
+
+ desc->dev_sub_class = lu_desc.bDeviceSubClass;
+ desc->dev_protocol = lu_desc.bDeviceProtocol;
+ desc->max_packet_size = lu_desc.bMaxPacketSize0;
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_get_descriptor: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+}
diff --git a/sanei/sanei_wire.c b/sanei/sanei_wire.c
new file mode 100644
index 0000000..a43cc2e
--- /dev/null
+++ b/sanei/sanei_wire.c
@@ -0,0 +1,696 @@
+/* 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_wire.h"
+
+#define BACKEND_NAME sanei_wire
+#include "../include/sane/sanei_backend.h"
+
+void
+sanei_w_space (Wire * w, size_t howmuch)
+{
+ size_t nbytes, left_over;
+ int fd = w->io.fd;
+ ssize_t nread, nwritten;
+
+ DBG (3, "sanei_w_space: %lu bytes for wire %d\n", (u_long) howmuch, fd);
+
+ if (howmuch > w->buffer.size)
+ DBG (2, "sanei_w_space: bigger than buffer (%lu bytes), "
+ "may be flush()\n", (u_long) w->buffer.size);
+
+ if (w->status != 0)
+ {
+ DBG (1, "sanei_w_space: wire is in invalid state %d\n",
+ w->status);
+ return;
+ }
+
+ if (w->buffer.curr + howmuch > w->buffer.end)
+ {
+ DBG (4, "sanei_w_space: free buffer size is %lu\n",
+ (u_long) (w->buffer.end - w->buffer.curr));
+ switch (w->direction)
+ {
+ case WIRE_ENCODE:
+ nbytes = w->buffer.curr - w->buffer.start;
+ w->buffer.curr = w->buffer.start;
+ DBG (4, "sanei_w_space: ENCODE: sending %lu bytes\n",
+ (u_long) nbytes);
+ while (nbytes > 0)
+ {
+ nwritten = (*w->io.write) (fd, w->buffer.curr, nbytes);
+ if (nwritten < 0)
+ {
+ DBG (1, "sanei_w_space: ENCODE: write failed (%d)\n", errno);
+ w->status = errno;
+ return;
+ }
+ w->buffer.curr += nwritten;
+ nbytes -= nwritten;
+ }
+ w->buffer.curr = w->buffer.start;
+ w->buffer.end = w->buffer.start + w->buffer.size;
+ DBG (4, "sanei_w_space: ENCODE: free buffer is now %lu\n",
+ (u_long) w->buffer.size);
+ break;
+
+ case WIRE_DECODE:
+ left_over = w->buffer.end - w->buffer.curr;
+
+ if ((signed) left_over < 0)
+ {
+ DBG (1, "sanei_w_space: DECODE: buffer underflow\n");
+ return;
+ }
+
+ if (left_over)
+ {
+ DBG (4, "sanei_w_space: DECODE: %lu bytes left in buffer\n",
+ (u_long) left_over);
+ memmove (w->buffer.start, w->buffer.curr, left_over);
+ }
+ w->buffer.curr = w->buffer.start;
+ w->buffer.end = w->buffer.start + left_over;
+
+ DBG (4, "sanei_w_space: DECODE: receiving data\n");
+ do
+ {
+ nread = (*w->io.read) (fd, w->buffer.end,
+ w->buffer.size - left_over);
+ if (nread <= 0)
+ {
+ DBG (2, "sanei_w_space: DECODE: no data received (%d)\n",
+ errno);
+ if (nread == 0)
+ errno = EINVAL;
+ w->status = errno;
+ return;
+ }
+ left_over += nread;
+ w->buffer.end += nread;
+ }
+ while (left_over < howmuch);
+ DBG (4, "sanei_w_space: DECODE: %lu bytes read\n",
+ (u_long) (w->buffer.end - w->buffer.start));
+ break;
+
+ case WIRE_FREE:
+ DBG (4, "sanei_w_space: FREE: doing nothing for free operation\n");
+ break;
+ }
+ }
+ DBG (4, "sanei_w_space: done\n");
+}
+
+void
+sanei_w_void (Wire * w)
+{
+ DBG (3, "sanei_w_void: wire %d (void debug output)\n", w->io.fd);
+}
+
+void
+sanei_w_array (Wire * w, SANE_Word * len_ptr, void **v,
+ WireCodecFunc w_element, size_t element_size)
+{
+ SANE_Word len;
+ char *val;
+ int i;
+
+ DBG (3, "sanei_w_array: wire %d, elements of size %lu\n", w->io.fd,
+ (u_long) element_size);
+
+ if (w->direction == WIRE_FREE)
+ {
+ if (*len_ptr && *v)
+ {
+ DBG (4, "sanei_w_array: FREE: freeing array (%d elements)\n",
+ *len_ptr);
+ val = *v;
+ for (i = 0; i < *len_ptr; ++i)
+ {
+ (*w_element) (w, val);
+ val += element_size;
+ }
+ free (*v);
+ w->allocated_memory -= (*len_ptr * element_size);
+ }
+ else
+ DBG (1, "sanei_w_array: FREE: tried to free array but *len_ptr or *v "
+ "was NULL\n");
+
+ DBG (4, "sanei_w_array: FREE: done\n");
+ return;
+ }
+
+ if (w->direction == WIRE_ENCODE)
+ len = *len_ptr;
+ DBG (4, "sanei_w_array: send/receive array length\n");
+ sanei_w_word (w, &len);
+
+ if (w->status)
+ {
+ DBG (1, "sanei_w_array: bad status: %d\n", w->status);
+ return;
+ }
+ DBG (4, "sanei_w_array: array has %d elements\n", len);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ *len_ptr = len;
+ if (len)
+ {
+ if (((unsigned int) len) > MAX_MEM
+ || ((unsigned int) len * element_size) > MAX_MEM
+ || (w->allocated_memory + len * element_size) > MAX_MEM)
+ {
+ DBG (0, "sanei_w_array: DECODE: maximum amount of allocated memory "
+ "exceeded (limit: %u, new allocation: %lu, total: %lu bytes)\n",
+ MAX_MEM, (unsigned long)(len * element_size),
+ (unsigned long)(MAX_MEM + len * element_size));
+ w->status = ENOMEM;
+ return;
+ }
+ *v = malloc (len * element_size);
+ if (*v == 0)
+ {
+ /* Malloc failed, so return an error. */
+ DBG (1, "sanei_w_array: DECODE: not enough free memory\n");
+ w->status = ENOMEM;
+ return;
+ }
+ memset (*v, 0, len * element_size);
+ w->allocated_memory += (len * element_size);
+ }
+ else
+ *v = 0;
+ }
+
+ val = *v;
+ DBG (4, "sanei_w_array: transferring array elements\n");
+ for (i = 0; i < len; ++i)
+ {
+ (*w_element) (w, val);
+ val += element_size;
+ if (w->status)
+ {
+ DBG (1, "sanei_w_array: bad status: %d\n", w->status);
+ return;
+ }
+ }
+ DBG (4, "sanei_w_array: done\n");
+}
+
+void
+sanei_w_ptr (Wire * w, void **v, WireCodecFunc w_value, size_t value_size)
+{
+ SANE_Word is_null;
+
+ DBG (3, "sanei_w_ptr: wire %d, value pointer at is %lu bytes\n", w->io.fd,
+ (u_long) value_size);
+
+ if (w->direction == WIRE_FREE)
+ {
+ if (*v && value_size)
+ {
+ DBG (4, "sanei_w_ptr: FREE: freeing value\n");
+ (*w_value) (w, *v);
+ free (*v);
+ w->allocated_memory -= value_size;
+ }
+ else
+ DBG (1, "sanei_w_ptr: FREE: tried to free value but *v or value_size "
+ "was NULL\n");
+
+ DBG (4, "sanei_w_ptr: FREE: done\n");
+ return;
+ }
+ if (w->direction == WIRE_ENCODE)
+ is_null = (*v == 0);
+
+ DBG (4, "sanei_w_ptr: send/receive is_null\n");
+ sanei_w_word (w, &is_null);
+ if (w->status)
+ {
+ DBG (1, "sanei_w_ptr: bad status: %d\n", w->status);
+ return;
+ }
+
+ if (!is_null)
+ {
+ if (w->direction == WIRE_DECODE)
+ {
+ DBG (4, "sanei_w_ptr: DECODE: receive data pointed at\n");
+ if (value_size > MAX_MEM)
+ {
+ DBG (0, "sanei_w_ptr: DECODE: maximum amount of allocated memory "
+ "exceeded (limit: %u, new allocation: %lu, total: %lu bytes)\n",
+ MAX_MEM, (unsigned long)value_size,
+ (unsigned long)(w->allocated_memory + value_size));
+ w->status = ENOMEM;
+ return;
+ }
+
+ *v = malloc (value_size);
+ if (*v == 0)
+ {
+ /* Malloc failed, so return an error. */
+ DBG (1, "sanei_w_ptr: DECODE: not enough free memory\n");
+ w->status = ENOMEM;
+ return;
+ }
+ w->allocated_memory += value_size;
+ memset (*v, 0, value_size);
+ }
+ (*w_value) (w, *v);
+ }
+ else if (w->direction == WIRE_DECODE)
+ *v = 0;
+
+ DBG (4, "sanei_w_ptr: done\n");
+}
+
+void
+sanei_w_byte (Wire * w, SANE_Byte * v)
+{
+ DBG (3, "sanei_w_byte: wire %d\n", w->io.fd);
+ (*w->codec.w_byte) (w, v);
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_byte: value = %d\n", *v);
+}
+
+void
+sanei_w_char (Wire * w, SANE_Char * v)
+{
+ DBG (3, "sanei_w_char: wire %d\n", w->io.fd);
+ (*w->codec.w_char) (w, v);
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_char: value = %d\n", *v);
+}
+
+void
+sanei_w_word (Wire * w, SANE_Word * v)
+{
+ DBG (3, "sanei_w_word: wire %d\n", w->io.fd);
+ (*w->codec.w_word) (w, v);
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_word: value = %d\n", *v);
+}
+
+void
+sanei_w_string (Wire * w, SANE_String * v)
+{
+ DBG (3, "sanei_w_string: wire %d\n", w->io.fd);
+ (*w->codec.w_string) (w, v);
+ if (w->direction != WIRE_FREE && w->status == 0)
+ DBG (4, "sanei_w_string: value = %s\n", *v);
+}
+
+void
+sanei_w_status (Wire * w, SANE_Status * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_status: wire %d\n", w->io.fd);
+
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_status: value = %d\n", word);
+}
+
+void
+sanei_w_bool (Wire * w, SANE_Bool * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_bool: wire %d\n", w->io.fd);
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_bool: value = %s\n",
+ ((word == SANE_TRUE) ? ("true") : ("false")));
+}
+
+void
+sanei_w_constraint_type (Wire * w, SANE_Constraint_Type * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_constraint_type: wire %d\n", w->io.fd);
+
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_constraint_type: value = %d\n", word);
+}
+
+void
+sanei_w_value_type (Wire * w, SANE_Value_Type * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_value_type: wire %d\n", w->io.fd);
+
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_value_type: value = %d\n", word);
+}
+
+void
+sanei_w_unit (Wire * w, SANE_Unit * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_unit: wire %d\n", w->io.fd);
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_unit: value = %d\n", word);
+ /* gosh... all the sane_w_something should be a macro or something */
+}
+
+void
+sanei_w_action (Wire * w, SANE_Action * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_action: wire %d\n", w->io.fd);
+
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_action: value = %d\n", word);
+}
+
+void
+sanei_w_frame (Wire * w, SANE_Frame * v)
+{
+ SANE_Word word = *v;
+
+ DBG (3, "sanei_w_frame: wire %d\n", w->io.fd);
+
+ sanei_w_word (w, &word);
+ if (w->direction == WIRE_DECODE)
+ *v = word;
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_frame: value = %d\n", word);
+}
+
+void
+sanei_w_range (Wire * w, SANE_Range * v)
+{
+ DBG (3, "sanei_w_range: wire %d\n", w->io.fd);
+ sanei_w_word (w, &v->min);
+ sanei_w_word (w, &v->max);
+ sanei_w_word (w, &v->quant);
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_range: min/max/step = %f/%f/%f\n",
+ SANE_UNFIX (v->min), SANE_UNFIX (v->max), SANE_UNFIX (v->quant));
+}
+
+void
+sanei_w_device (Wire * w, SANE_Device * v)
+{
+ DBG (3, "sanei_w_device: wire %d\n", w->io.fd);
+ sanei_w_string (w, (SANE_String *) & v->name);
+ sanei_w_string (w, (SANE_String *) & v->vendor);
+ sanei_w_string (w, (SANE_String *) & v->model);
+ sanei_w_string (w, (SANE_String *) & v->type);
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_device: %s %s from %s (%s)\n", v->name, v->model,
+ v->vendor, v->type);
+}
+
+void
+sanei_w_device_ptr (Wire * w, SANE_Device ** v)
+{
+ DBG (3, "sanei_w_device_ptr: wire %d\n", w->io.fd);
+ sanei_w_ptr (w, (void **) v, (WireCodecFunc) sanei_w_device, sizeof (**v));
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_device_ptr: device struct at %p\n", (void*) *v);
+}
+
+void
+sanei_w_option_descriptor (Wire * w, SANE_Option_Descriptor * v)
+{
+ SANE_Word len;
+
+ DBG (3, "sanei_w_option_descriptor: wire %d\n", w->io.fd);
+
+ sanei_w_string (w, (SANE_String *) & v->name);
+ sanei_w_string (w, (SANE_String *) & v->title);
+ sanei_w_string (w, (SANE_String *) & v->desc);
+ sanei_w_value_type (w, &v->type);
+ sanei_w_unit (w, &v->unit);
+ sanei_w_word (w, &v->size);
+ sanei_w_word (w, &v->cap);
+ sanei_w_constraint_type (w, &v->constraint_type);
+
+ if (w->direction != WIRE_FREE)
+ DBG (4, "sanei_w_option_descriptor: option %s\n", v->name);
+
+ switch (v->constraint_type)
+ {
+ case SANE_CONSTRAINT_NONE:
+ break;
+
+ case SANE_CONSTRAINT_RANGE:
+ sanei_w_ptr (w, (void **) &v->constraint.range,
+ (WireCodecFunc) sanei_w_range, sizeof (SANE_Range));
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (w->direction != WIRE_DECODE)
+ len = v->constraint.word_list[0] + 1;
+ sanei_w_array (w, &len, (void **) &v->constraint.word_list,
+ w->codec.w_word, sizeof (SANE_Word));
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ if (w->direction != WIRE_DECODE)
+ {
+ for (len = 0; v->constraint.string_list[len]; ++len);
+ ++len; /* send NULL string, too */
+ }
+ sanei_w_array (w, &len, (void **) &v->constraint.string_list,
+ w->codec.w_string, sizeof (SANE_String));
+ break;
+ }
+ DBG (4, "sanei_w_option_descriptor: done\n");
+}
+
+void
+sanei_w_option_descriptor_ptr (Wire * w, SANE_Option_Descriptor ** v)
+{
+ DBG (3, "sanei_w_option_descriptor_ptr: wire %d\n", w->io.fd);
+ sanei_w_ptr (w, (void **) v,
+ (WireCodecFunc) sanei_w_option_descriptor, sizeof (**v));
+ DBG (4, "sanei_w_option_descriptor_ptr: done\n");
+}
+
+void
+sanei_w_parameters (Wire * w, SANE_Parameters * v)
+{
+ DBG (3, "sanei_w_parameters: wire %d\n", w->io.fd);
+ sanei_w_frame (w, &v->format);
+ sanei_w_bool (w, &v->last_frame);
+ sanei_w_word (w, &v->bytes_per_line);
+ sanei_w_word (w, &v->pixels_per_line);
+ sanei_w_word (w, &v->lines);
+ sanei_w_word (w, &v->depth);
+ if (w->direction != WIRE_FREE)
+ DBG (4,
+ "sanei_w_parameters: format/last/bpl/ppl/lines/depth = "
+ "%d/%d/%d/%d/%d/%d\n", v->format, v->last_frame, v->bytes_per_line,
+ v->pixels_per_line, v->lines, v->depth);
+}
+
+static void
+flush (Wire * w)
+{
+ DBG (3, "flush: wire %d\n", w->io.fd);
+ if (w->direction == WIRE_ENCODE)
+ sanei_w_space (w, w->buffer.size + 1);
+ else if (w->direction == WIRE_DECODE)
+ w->buffer.curr = w->buffer.end = w->buffer.start;
+ if (w->status != 0)
+ DBG (2, "flush: error status %d\n", w->status);
+ DBG (4, "flush: wire flushed\n");
+}
+
+void
+sanei_w_set_dir (Wire * w, WireDirection dir)
+{
+ DBG (3, "sanei_w_set_dir: wire %d, old direction WIRE_%s\n", w->io.fd,
+ w->direction == WIRE_ENCODE ? "ENCODE" :
+ (w->direction == WIRE_DECODE ? "DECODE" : "FREE"));
+ if (w->direction == WIRE_DECODE && w->buffer.curr != w->buffer.end)
+ DBG (1, "sanei_w_set_dir: WARNING: will delete %lu bytes from buffer\n",
+ (u_long) (w->buffer.end - w->buffer.curr));
+ flush (w);
+ w->direction = dir;
+ DBG (4, "sanei_w_set_dir: direction changed\n");
+ flush (w);
+ DBG (3, "sanei_w_set_dir: wire %d, new direction WIRE_%s\n", w->io.fd,
+ dir == WIRE_ENCODE ? "ENCODE" :
+ (dir == WIRE_DECODE ? "DECODE" : "FREE"));
+}
+
+void
+sanei_w_call (Wire * w,
+ SANE_Word procnum,
+ WireCodecFunc w_arg, void *arg,
+ WireCodecFunc w_reply, void *reply)
+{
+
+ DBG (3, "sanei_w_call: wire %d (old status %d)\n", w->io.fd, w->status);
+ w->status = 0;
+ sanei_w_set_dir (w, WIRE_ENCODE);
+
+ DBG (4, "sanei_w_call: sending request (procedure number: %d)\n", procnum);
+ sanei_w_word (w, &procnum);
+ (*w_arg) (w, arg);
+
+ if (w->status == 0)
+ {
+ DBG (4, "sanei_w_call: receiving reply\n");
+ sanei_w_set_dir (w, WIRE_DECODE);
+ (*w_reply) (w, reply);
+ }
+
+ if (w->status != 0)
+ DBG (2, "sanei_w_call: error status %d\n", w->status);
+ DBG (4, "sanei_w_call: done\n");
+}
+
+void
+sanei_w_reply (Wire * w, WireCodecFunc w_reply, void *reply)
+{
+ DBG (3, "sanei_w_reply: wire %d (old status %d)\n", w->io.fd, w->status);
+ w->status = 0;
+ sanei_w_set_dir (w, WIRE_ENCODE);
+ (*w_reply) (w, reply);
+ flush (w);
+ if (w->status != 0)
+ DBG (2, "sanei_w_reply: error status %d\n", w->status);
+ DBG (4, "sanei_w_reply: done\n");
+}
+
+void
+sanei_w_free (Wire * w, WireCodecFunc w_reply, void *reply)
+{
+ WireDirection saved_dir = w->direction;
+
+ DBG (3, "sanei_w_free: wire %d\n", w->io.fd);
+
+ w->direction = WIRE_FREE;
+ (*w_reply) (w, reply);
+ w->direction = saved_dir;
+
+ if (w->status != 0)
+ DBG (2, "sanei_w_free: error status %d\n", w->status);
+ DBG (4, "sanei_w_free: done\n");
+}
+
+void
+sanei_w_init (Wire * w, void (*codec_init_func) (Wire *))
+{
+ DBG_INIT ();
+
+ DBG (3, "sanei_w_init: initializing\n");
+ w->status = 0;
+ w->direction = WIRE_ENCODE;
+ w->buffer.size = 8192;
+ w->buffer.start = malloc (w->buffer.size);
+
+ if (w->buffer.start == 0)
+ {
+ /* Malloc failed, so return an error. */
+ w->status = ENOMEM;
+ DBG (1, "sanei_w_init: not enough free memory\n");
+ }
+
+ w->buffer.curr = w->buffer.start;
+ w->buffer.end = w->buffer.start + w->buffer.size;
+ if (codec_init_func != 0)
+ {
+ DBG (4, "sanei_w_init: initializing codec\n");
+ (*codec_init_func) (w);
+ }
+ w->allocated_memory = 0;
+ DBG (4, "sanei_w_init: done\n");
+}
+
+void
+sanei_w_exit (Wire * w)
+{
+ DBG (3, "sanei_w_exit: wire %d\n", w->io.fd);
+ if (w->buffer.start)
+ {
+ DBG (4, "sanei_w_exit: freeing buffer\n");
+ free (w->buffer.start);
+ }
+ w->buffer.start = 0;
+ w->buffer.size = 0;
+ DBG (4, "sanei_w_exit: done\n");
+}