From 6e9c41a892ed0e0da326e0278b3221ce3f5713b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 6 Oct 2014 14:00:40 +0200 Subject: Initial import of sane-backends version 1.0.24-1.2 --- sanei/Makefile.am | 21 + sanei/Makefile.in | 675 +++++ sanei/linux_sg3_err.h | 135 + sanei/os2_srb.h | 156 ++ sanei/sanei_DomainOS.c | 528 ++++ sanei/sanei_DomainOS.h | 76 + sanei/sanei_ab306.c | 583 ++++ sanei/sanei_access.c | 232 ++ sanei/sanei_auth.c | 283 ++ sanei/sanei_codec_ascii.c | 345 +++ sanei/sanei_codec_bin.c | 139 + sanei/sanei_config.c | 453 +++ sanei/sanei_config2.c | 154 + sanei/sanei_constrain_value.c | 309 ++ sanei/sanei_init_debug.c | 150 + sanei/sanei_jpeg.c | 235 ++ sanei/sanei_lm983x.c | 265 ++ sanei/sanei_magic.c | 1941 +++++++++++++ sanei/sanei_net.c | 186 ++ sanei/sanei_pa4s2.c | 2103 ++++++++++++++ sanei/sanei_pio.c | 604 ++++ sanei/sanei_pp.c | 1462 ++++++++++ sanei/sanei_pv8630.c | 223 ++ sanei/sanei_scsi.c | 6187 +++++++++++++++++++++++++++++++++++++++++ sanei/sanei_tcp.c | 136 + sanei/sanei_thread.c | 562 ++++ sanei/sanei_udp.c | 232 ++ sanei/sanei_usb.c | 3151 +++++++++++++++++++++ sanei/sanei_wire.c | 696 +++++ 29 files changed, 22222 insertions(+) create mode 100644 sanei/Makefile.am create mode 100644 sanei/Makefile.in create mode 100644 sanei/linux_sg3_err.h create mode 100644 sanei/os2_srb.h create mode 100755 sanei/sanei_DomainOS.c create mode 100755 sanei/sanei_DomainOS.h create mode 100644 sanei/sanei_ab306.c create mode 100644 sanei/sanei_access.c create mode 100644 sanei/sanei_auth.c create mode 100644 sanei/sanei_codec_ascii.c create mode 100644 sanei/sanei_codec_bin.c create mode 100644 sanei/sanei_config.c create mode 100644 sanei/sanei_config2.c create mode 100644 sanei/sanei_constrain_value.c create mode 100644 sanei/sanei_init_debug.c create mode 100644 sanei/sanei_jpeg.c create mode 100644 sanei/sanei_lm983x.c create mode 100644 sanei/sanei_magic.c create mode 100644 sanei/sanei_net.c create mode 100644 sanei/sanei_pa4s2.c create mode 100644 sanei/sanei_pio.c create mode 100644 sanei/sanei_pp.c create mode 100644 sanei/sanei_pv8630.c create mode 100644 sanei/sanei_scsi.c create mode 100644 sanei/sanei_tcp.c create mode 100644 sanei/sanei_thread.c create mode 100644 sanei/sanei_udp.c create mode 100644 sanei/sanei_usb.c create mode 100644 sanei/sanei_wire.c (limited to 'sanei') 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#include + +#ifdef HAVE_SYS_IO_H +# include /* use where available (glibc 2.x, for example) */ +#elif HAVE_ASM_IO_H +# include /* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, 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 + 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, of which + every one has 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 +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#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 + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + + +#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 +#include +#include +#include + +#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 +#include +#include + +#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 +#include +#include +#include + +#include + +#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 +#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 +#include +#include +#include + +#ifdef __BEOS__ +#include +#include +#include +#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 + +#include +#include + +#include + +#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 +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_VSYSLOG +#include +#endif +#ifdef HAVE_OS2_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include + +#ifdef HAVE_OS2_H +# define INCL_DOS +# include +#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<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 + + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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; iformat == SANE_FRAME_GRAY && params->depth == 8){ + for(i=bw; iformat == SANE_FRAME_GRAY && params->depth == 1){ + for(i=bw; i> (7-(j+l)%8) & 1; + } + } + + if(!curr) + continue; + + /*loop over rows and columns around window */ + for(k=-1; k> (7-(j+l)%8) & 1; + + if(hits) + break; + } + } + + /*no hits, overwrite with white*/ + if(!hits){ + for(k=0; kpixels_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 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 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; ilines = 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= pwidth) + continue; + + sourceY = centerY + (int)(-shiftY * slopeCos + shiftX * slopeSin); + if (sourceY < 0 || sourceY >= height) + continue; + + for (k=0; kformat == SANE_FRAME_GRAY && params->depth == 1){ + + if(bg_color) + bg_color = 0xff; + + memset(outbuf,bg_color,bwidth*height); + + for (i=0; i= 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; ilines; 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; jbytes_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; ilines; i++){ + int rowsum = 0; + SANE_Byte * ptr = buffer + params->bytes_per_line*i; + + /* loop over all columns, sum the pixels */ + for(j=0; jpixels_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; ybbytes_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 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; ybbytes_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> (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; ilines; 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; jpixels_per_line; j++){ + int curr = 0; + + /*convert color to gray*/ + for (k=0; k 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; ipixels_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; jlines; j++){ + int curr = 0; + + /*convert color to gray*/ + for (k=0; kbytes_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; ilines; 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; jpixels_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; ipixels_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; jlines; 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; iformat == SANE_FRAME_GRAY && params->depth == 1){ + + switch (angle) { + + /*rotate 90 clockwise*/ + case 1: + for (i=0; i> (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> (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> (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= 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 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 *finDensity){ + *finDensity = lines[i][j]; + *finSlope = slopeCenter[i]; + *finOffset = offsetCenter[j]; + } + } + } + + if(0){ + fprintf(stderr,"offsetCenter: "); + for(j=0;j 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 -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; iformat == 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){ + farLine = firstLine; + } + if(nearLine < 0 || nearLine >= height){ + nearLine = firstLine; + } + + for(k=0; k 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> (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;ibytes_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; iformat == 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){ + farCol = firstCol; + } + if(nearCol < 0 || nearCol >= width){ + nearCol = firstCol; + } + + for(k=0; k 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> (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 +#include + +#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 + 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 +#endif + +#if defined(HAVE_LIBIEEE1284) + +# include + +#elif defined(ENABLE_PARPORT_DIRECTIO) + +# if defined(HAVE_SYS_IO_H) +# if defined (__ICC) && __ICC >= 700 +# define __GNUC__ 2 +# endif +# include +# if defined (__ICC) && __ICC >= 700 +# undef __GNUC__ +# elif defined(__ICC) && defined(HAVE_ASM_IO_H) +# include +# endif +# elif defined(HAVE_ASM_IO_H) +# include /* ugly, but backwards compatible */ +# elif defined(HAVE_SYS_HW_H) +# include +# 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 +# include +# include +#endif +#if defined(HAVE_STRING_H) +# include +#elif defined(HAVE_STRINGS_H) +# include +#endif +#if defined(HAVE_SYS_TYPES_H) +# include +#endif + +#include "../include/sane/saneopts.h" + + +#if (defined (HAVE_IOPERM) || defined (HAVE_LIBIEEE1284)) && !defined (IO_SUPPORT_MISSING) + +#if defined(STDC_HEADERS) +# include +# include +# include +#endif +#if defined(HAVE_STRING_H) +# include +#elif defined(HAVE_STRINGS_H) +# include +#endif +#if defined(HAVE_SYS_TYPES_H) +# include +#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; nname); + + + 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; nname; + +#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= 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 +#endif +#include + +#ifdef HAVE_SYS_IO_H +# include /* use where available (glibc 2.x, for example) */ +#elif HAVE_ASM_IO_H +# include /* ugly, but backwards compatible */ +#elif HAVE_SYS_HW_H +# include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, 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 + +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 + * based on work done by Jochen Eisinger + * also parts from libieee1284 by Tim Waugh + * This file is part of the SANE package. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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 +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_LIMITS_H +# include +#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 +# if defined (__ICC) && __ICC >= 700 +# undef __GNUC__ +# elif defined(__ICC) && defined(HAVE_ASM_IO_H) +# include +# endif +# elif defined(HAVE_ASM_IO_H) +# include +# elif defined(HAVE_SYS_HW_H) +# include +# 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 +#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 +# include +# include +#endif +#if defined(HAVE_STRING_H) +# include +#elif defined(HAVE_STRINGS_H) +# include +#endif +#if defined(HAVE_SYS_TYPES_H) +# include +#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 +#include + +#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 +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include + +#if defined (HAVE_WINDOWS_H) +# include +#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 +#endif + +#if defined (HAVE_SCSI_SG_H) +# define USE LINUX_INTERFACE +# include +#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 +# include +#elif defined (HAVE_CAMLIB_H) +# define USE FREEBSD_CAM_INTERFACE +# include /* there is a bug in scsi_all.h */ +# include +# include +# include +# include +# include +#elif defined (HAVE_SYS_SCSIIO_H) +# define USE BSD_INTERFACE +# include +# ifdef HAVE_SCSI_H +# include +# endif +#elif defined (HAVE_BSD_DEV_SCSIREG_H) +# define USE OPENSTEP_INTERFACE +# include +#elif defined (HAVE_IO_CAM_CAM_H) +# define USE DECUNIX_INTERFACE +# include +# include +# include +# include +# include +#elif defined (HAVE_SYS_DSREQ_H) +# define USE IRIX_INTERFACE +# include +# include +#elif defined (HAVE_SYS_SCSI_H) +# include +# ifdef HAVE_SYS_SDI_COMM_H +# ifdef HAVE_SYS_PASSTHRUDEF_H +# define USE SCO_UW71_INTERFACE +# include +# include +# include +# include +# else +# define USE SYSVR4_INTERFACE /* Unixware 2.x tested */ +# define HAVE_SYSV_DRIVER +# include +# include +# endif +# else +# ifdef SCTL_READ +# define USE HPUX_INTERFACE +# else +# ifdef HAVE_GSCDDS_H +# define USE AIX_GSC_INTERFACE +# include +# 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 +# include "os2_srb.h" +#elif defined (HAVE_SYS_SCSI_SGDEFS_H) +# define USE SOLARIS_SG_INTERFACE +# include +#elif defined (HAVE_SYS_SCSI_TARGETS_SCGIO_H) +# define USE SOLARIS_INTERFACE +# define SOL2 +# include +#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 +#elif defined (HAVE_APOLLO_SCSI_H) +# define USE DOMAINOS_INTERFACE +# include /* Only used for signal name for KillDomainServer */ +# include +# include +# include +# include +# include +# include +# include +# 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 +# include +# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H +# include +# include +# endif +# ifdef HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H +/* The def of VERSION causes problems in the following include files */ +# undef VERSION +# include +# include +# include +# else +# ifdef HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H +/* The def of VERSION causes problems in the following include files */ +# undef VERSION +# include +# include +# include +# endif +# endif +#elif defined (HAVE_DDK_NTDDSCSI_H) +# define USE WIN32_INTERFACE +# include +# include +#elif defined (HAVE_NTDDSCSI_H) +# define USE WIN32_INTERFACE +# include +#endif + +#ifndef USE +# define USE STUBBED_INTERFACE +#endif + +#if USE == LINUX_INTERFACE +# include +#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" 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 +#include + +#include + +#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. ) 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 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 */ + 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 + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. */ + +#include "../include/sane/config.h" + +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK2_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#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 +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_OS2_H +# define INCL_DOSPROCESS +# include +#endif +#ifdef __BEOS__ +# undef USE_PTHREAD /* force */ +# include +#endif +#if !defined USE_PTHREAD && !defined HAVE_OS2_H && !defined __BEOS__ +# include +#endif +#if defined USE_PTHREAD +# include +#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 + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. */ + +#include "../include/sane/config.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK2_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#include +#include +#include +#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 (OS/2 usbcalls) + Copyright (C) 2008 m. allan noah (bus rescan support, sanei_usb_clear_halt) + Copyright (C) 2009 Julien BLACHE (libusb-1.0) + Copyright (C) 2011 Reinhold Kainhofer (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 +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#include +#include + +/* for debug messages */ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + + +#ifdef HAVE_RESMGR +#include +#endif + +#ifdef HAVE_LIBUSB +#ifdef HAVE_LUSB0_USB_H +#include +#else +#include +#endif +#endif /* HAVE_LIBUSB */ + +#ifdef HAVE_LIBUSB_1_0 +#include +#endif /* HAVE_LIBUSB_1_0 */ + +#ifdef HAVE_USBCALLS +#include +#include +#include +#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 +#include +#endif /* __FreeBSD__ */ +#if defined (__DragonFly__) +#include +#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 +#include +#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 +#include +#include + +#include + +#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"); +} -- cgit v1.2.3