summaryrefslogtreecommitdiff
path: root/sanei
diff options
context:
space:
mode:
Diffstat (limited to 'sanei')
-rw-r--r--sanei/Makefile.am2
-rw-r--r--sanei/Makefile.in17
-rw-r--r--sanei/sanei_ab306.c3
-rw-r--r--sanei/sanei_ir.c1205
-rw-r--r--sanei/sanei_pa4s2.c5
-rw-r--r--sanei/sanei_pio.c3
-rw-r--r--sanei/sanei_pp.c3
-rw-r--r--sanei/sanei_thread.c14
-rw-r--r--sanei/sanei_usb.c171
9 files changed, 1331 insertions, 92 deletions
diff --git a/sanei/Makefile.am b/sanei/Makefile.am
index c466e44..c1106ae 100644
--- a/sanei/Makefile.am
+++ b/sanei/Makefile.am
@@ -14,7 +14,7 @@ libsanei_la_SOURCES = sanei_ab306.c sanei_constrain_value.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_udp.c sanei_magic.c sanei_ir.c
if HAVE_JPEG
libsanei_la_SOURCES += sanei_jpeg.c
endif
diff --git a/sanei/Makefile.in b/sanei/Makefile.in
index 66fc549..1fba44d 100644
--- a/sanei/Makefile.in
+++ b/sanei/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -102,7 +102,7 @@ am__libsanei_la_SOURCES_DIST = sanei_ab306.c sanei_constrain_value.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
+ sanei_udp.c sanei_magic.c sanei_ir.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 \
@@ -110,7 +110,7 @@ am_libsanei_la_OBJECTS = sanei_ab306.lo sanei_constrain_value.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)
+ sanei_udp.lo sanei_magic.lo sanei_ir.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@)
@@ -269,6 +269,7 @@ PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_LIBS = @PNG_LIBS@
PRELOADABLE_BACKENDS = @PRELOADABLE_BACKENDS@
PRELOADABLE_BACKENDS_ENABLED = @PRELOADABLE_BACKENDS_ENABLED@
PTHREAD_LIBS = @PTHREAD_LIBS@
@@ -280,11 +281,14 @@ SCSI_LIBS = @SCSI_LIBS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
+SNMP_CFLAGS = @SNMP_CFLAGS@
SNMP_CONFIG_PATH = @SNMP_CONFIG_PATH@
+SNMP_LIBS = @SNMP_LIBS@
SOCKET_LIBS = @SOCKET_LIBS@
STRICT_LDFLAGS = @STRICT_LDFLAGS@
STRIP = @STRIP@
SYSLOG_LIBS = @SYSLOG_LIBS@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
SYSTEMD_LIBS = @SYSTEMD_LIBS@
TIFF_LIBS = @TIFF_LIBS@
USB_LIBS = @USB_LIBS@
@@ -358,7 +362,7 @@ libsanei_la_SOURCES = sanei_ab306.c sanei_constrain_value.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)
+ sanei_udp.c sanei_magic.c sanei_ir.c $(am__append_1)
EXTRA_DIST = linux_sg3_err.h os2_srb.h sanei_DomainOS.c sanei_DomainOS.h
all: all-am
@@ -424,6 +428,7 @@ distclean-compile:
@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_ir.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@
@@ -444,14 +449,14 @@ distclean-compile:
@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 $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.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) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/sanei/sanei_ab306.c b/sanei/sanei_ab306.c
index 91d647d..c663d38 100644
--- a/sanei/sanei_ab306.c
+++ b/sanei/sanei_ab306.c
@@ -51,6 +51,9 @@
#ifdef HAVE_SYS_IO_H
# include <sys/io.h> /* use where available (glibc 2.x, for example) */
+# ifndef SANE_HAVE_SYS_IO_H_WITH_INB_OUTB
+# define IO_SUPPORT_MISSING
+# endif
#elif HAVE_ASM_IO_H
# include <asm/io.h> /* ugly, but backwards compatible */
#elif defined (__i386__) && defined (__GNUC__)
diff --git a/sanei/sanei_ir.c b/sanei/sanei_ir.c
new file mode 100644
index 0000000..42e82ba
--- /dev/null
+++ b/sanei/sanei_ir.c
@@ -0,0 +1,1205 @@
+/** @file sanei_ir.c
+ *
+ * sanei_ir - functions for utilizing the infrared plane
+ *
+ * Copyright (C) 2012 Michael Rickmann <mrickma@gwdg.de>
+ *
+ * This file is part of the SANE package.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * The threshold yen, otsu and max_entropy routines have been
+ * adapted from the FOURIER 0.8 library by M. Emre Celebi,
+ * http://sourceforge.net/projects/fourier-ipal/ which is
+ * licensed under the GNU General Public License version 2 or later.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <values.h>
+#include <math.h>
+
+#define BACKEND_NAME sanei_ir /* name of this module for debugging */
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_ir.h"
+#include "../include/sane/sanei_magic.h"
+
+
+double *
+sanei_ir_create_norm_histo (const SANE_Parameters * params, const SANE_Uint *img_data);
+double * sanei_ir_accumulate_norm_histo (double * histo_data);
+
+
+/* Initialize sanei_ir
+ */
+void
+sanei_ir_init (void)
+{
+ DBG_INIT ();
+}
+
+
+/* Create a normalized histogram of a grayscale image, internal
+ */
+double *
+sanei_ir_create_norm_histo (const SANE_Parameters * params,
+ const SANE_Uint *img_data)
+{
+ int is, i;
+ int num_pixels;
+ int *histo_data;
+ double *histo;
+ double term;
+
+ DBG (10, "sanei_ir_create_norm_histo\n");
+
+ if ((params->format != SANE_FRAME_GRAY)
+ && (params->format != SANE_FRAME_RED)
+ && (params->format != SANE_FRAME_GREEN)
+ && (params->format != SANE_FRAME_BLUE))
+ {
+ DBG (5, "sanei_ir_create_norm_histo: invalid format\n");
+ return NULL;
+ }
+
+ /* Allocate storage for the histogram */
+ histo_data = calloc (HISTOGRAM_SIZE, sizeof (int));
+ histo = malloc (HISTOGRAM_SIZE * sizeof (double));
+ if ((histo == NULL) || (histo_data == NULL))
+ {
+ DBG (5, "sanei_ir_create_norm_histo: no buffers\n");
+ if (histo) free (histo);
+ if (histo_data) free (histo_data);
+ return NULL;
+ }
+
+ num_pixels = params->pixels_per_line * params->lines;
+
+ DBG (1, "sanei_ir_create_norm_histo: %d pixels_per_line, %d lines => %d num_pixels\n", params->pixels_per_line, params->lines, num_pixels);
+ DBG (1, "sanei_ir_create_norm_histo: histo_data[] with %d x %ld bytes\n", HISTOGRAM_SIZE, sizeof(int));
+ /* Populate the histogram */
+ is = 16 - HISTOGRAM_SHIFT; /* Number of data bits to ignore */
+ DBG (1, "sanei_ir_create_norm_histo: depth %d, HISTOGRAM_SHIFT %d => ignore %d bits\n", params->depth, HISTOGRAM_SHIFT, is);
+ for (i = num_pixels; i > 0; i--) {
+ histo_data[*img_data++ >> is]++;
+ }
+
+ /* Calculate the normalized histogram */
+ term = 1.0 / (double) num_pixels;
+ for (i = 0; i < HISTOGRAM_SIZE; i++)
+ histo[i] = term * (double) histo_data[i];
+
+ free (histo_data);
+ return histo;
+}
+
+
+/* Create the normalized histogram of a grayscale image
+ */
+SANE_Status
+sanei_ir_create_norm_histogram (const SANE_Parameters * params,
+ const SANE_Uint *img_data,
+ double ** histogram)
+{
+ double *histo;
+
+ DBG (10, "sanei_ir_create_norm_histogram\n");
+
+ histo = sanei_ir_create_norm_histo (params, img_data);
+ if (!histo)
+ return SANE_STATUS_NO_MEM;
+
+ *histogram = histo;
+ return SANE_STATUS_GOOD;
+}
+
+/* Accumulate a normalized histogram, internal
+ */
+double *
+sanei_ir_accumulate_norm_histo (double * histo_data)
+{
+ int i;
+ double *accum_data;
+
+ accum_data = malloc (HISTOGRAM_SIZE * sizeof (double));
+ if (accum_data == NULL)
+ {
+ DBG (5, "sanei_ir_accumulate_norm_histo: Insufficient memory !\n");
+ return NULL;
+ }
+
+ accum_data[0] = histo_data[0];
+ for (i = 1; i < HISTOGRAM_SIZE; i++)
+ accum_data[i] = accum_data[i - 1] + histo_data[i];
+
+ return accum_data;
+}
+
+/* Implements Yen's thresholding method
+ */
+SANE_Status
+sanei_ir_threshold_yen (const SANE_Parameters * params,
+ double * norm_histo, int *thresh)
+{
+ double *P1 = NULL; /* cumulative normalized histogram */
+ double *P1_sq = NULL; /* cumulative normalized histogram */
+ double *P2_sq = NULL;
+ double crit, max_crit;
+ int threshold, i;
+ SANE_Status ret = SANE_STATUS_NO_MEM;
+
+ DBG (10, "sanei_ir_threshold_yen\n");
+
+ P1 = sanei_ir_accumulate_norm_histo (norm_histo);
+ P1_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
+ P2_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
+ if (!P1 || !P1_sq || !P2_sq)
+ {
+ DBG (5, "sanei_ir_threshold_yen: no buffers\n");
+ goto cleanup;
+ }
+
+ /* calculate cumulative squares */
+ P1_sq[0] = norm_histo[0] * norm_histo[0];
+ for (i = 1; i < HISTOGRAM_SIZE; i++)
+ P1_sq[i] = P1_sq[i - 1] + norm_histo[i] * norm_histo[i];
+ P2_sq[HISTOGRAM_SIZE - 1] = 0.0;
+ for (i = HISTOGRAM_SIZE - 2; i >= 0; i--)
+ P2_sq[i] = P2_sq[i + 1] + norm_histo[i + 1] * norm_histo[i + 1];
+
+ /* Find the threshold that maximizes the criterion */
+ threshold = INT_MIN;
+ max_crit = DBL_MIN;
+ for (i = 0; i < HISTOGRAM_SIZE; i++)
+ {
+ crit =
+ -1.0 * SAFE_LOG (P1_sq[i] * P2_sq[i]) +
+ 2 * SAFE_LOG (P1[i] * (1.0 - P1[i]));
+ if (crit > max_crit)
+ {
+ max_crit = crit;
+ threshold = i;
+ }
+ }
+
+ if (threshold == INT_MIN)
+ {
+ DBG (5, "sanei_ir_threshold_yen: no threshold found\n");
+ ret = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ ret = SANE_STATUS_GOOD;
+ if (params->depth > 8)
+ {
+ i = 1 << (params->depth - HISTOGRAM_SHIFT);
+ *thresh = threshold * i + i / 2;
+ }
+ else
+ *thresh = threshold;
+ DBG (10, "sanei_ir_threshold_yen: threshold %d\n", *thresh);
+ }
+
+ cleanup:
+ if (P1)
+ free (P1);
+ if (P1_sq)
+ free (P1_sq);
+ if (P2_sq)
+ free (P2_sq);
+ return ret;
+}
+
+
+/* Implements Otsu's thresholding method
+ */
+SANE_Status
+sanei_ir_threshold_otsu (const SANE_Parameters * params,
+ double * norm_histo, int *thresh)
+{
+ double *cnh = NULL;
+ double *mean = NULL;
+ double total_mean;
+ double bcv, max_bcv;
+ int first_bin, last_bin;
+ int threshold, i;
+ SANE_Status ret = SANE_STATUS_NO_MEM;
+
+ DBG (10, "sanei_ir_threshold_otsu\n");
+
+ cnh = sanei_ir_accumulate_norm_histo (norm_histo);
+ mean = malloc (HISTOGRAM_SIZE * sizeof (double));
+ if (!cnh || !mean)
+ {
+ DBG (5, "sanei_ir_threshold_otsu: no buffers\n");
+ goto cleanup;
+ }
+
+ mean[0] = 0.0;
+ for (i = 1; i < HISTOGRAM_SIZE; i++)
+ mean[i] = mean[i - 1] + i * norm_histo[i];
+ total_mean = mean[HISTOGRAM_SIZE - 1];
+
+ first_bin = 0;
+ for (i = 0; i < HISTOGRAM_SIZE; i++)
+ if (cnh[i] != 0)
+ {
+ first_bin = i;
+ break;
+ }
+ last_bin = HISTOGRAM_SIZE - 1;
+ for (i = HISTOGRAM_SIZE - 1; i >= first_bin; i--)
+ if (1.0 - cnh[i] != 0)
+ {
+ last_bin = i;
+ break;
+ }
+
+ threshold = INT_MIN;
+ max_bcv = 0.0;
+ for (i = first_bin; i <= last_bin; i++)
+ {
+ bcv = total_mean * cnh[i] - mean[i];
+ bcv *= bcv / (cnh[i] * (1.0 - cnh[i]));
+ if (max_bcv < bcv)
+ {
+ max_bcv = bcv;
+ threshold = i;
+ }
+ }
+
+ if (threshold == INT_MIN)
+ {
+ DBG (5, "sanei_ir_threshold_otsu: no threshold found\n");
+ ret = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ ret = SANE_STATUS_GOOD;
+ if (params->depth > 8)
+ {
+ i = 1 << (params->depth - HISTOGRAM_SHIFT);
+ *thresh = threshold * i + i / 2;
+ }
+ else
+ *thresh = threshold;
+ DBG (10, "sanei_ir_threshold_otsu: threshold %d\n", *thresh);
+ }
+ cleanup:
+ if (cnh)
+ free (cnh);
+ if (mean)
+ free (mean);
+ return ret;
+}
+
+
+/* Implements a Maximum Entropy thresholding method
+ */
+SANE_Status
+sanei_ir_threshold_maxentropy (const SANE_Parameters * params,
+ double * norm_histo, int *thresh)
+{
+ int ih, it;
+ int threshold;
+ int first_bin;
+ int last_bin;
+ double tot_ent, max_ent; /* entropies */
+ double ent_back, ent_obj;
+ double *P1; /* cumulative normalized histogram */
+ double *P2;
+ SANE_Status ret = SANE_STATUS_NO_MEM;
+
+ DBG (10, "sanei_ir_threshold_maxentropy\n");
+
+ /* Calculate the cumulative normalized histogram */
+ P1 = sanei_ir_accumulate_norm_histo (norm_histo);
+ P2 = malloc (HISTOGRAM_SIZE * sizeof (double));
+ if (!P1 || !P2)
+ {
+ DBG (5, "sanei_ir_threshold_maxentropy: no buffers\n");
+ goto cleanup;
+ }
+
+ for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
+ P2[ih] = 1.0 - P1[ih];
+
+ first_bin = 0;
+ for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
+ if (P1[ih] != 0)
+ {
+ first_bin = ih;
+ break;
+ }
+ last_bin = HISTOGRAM_SIZE - 1;
+ for ( ih = HISTOGRAM_SIZE - 1; ih >= first_bin; ih-- )
+ if (P2[ih] != 0)
+ {
+ last_bin = ih;
+ break;
+ }
+
+ /* Calculate the total entropy each gray-level
+ * and find the threshold that maximizes it
+ */
+ threshold = INT_MIN;
+ max_ent = DBL_MIN;
+ for ( it = first_bin; it <= last_bin; it++ )
+ {
+ /* Entropy of the background pixels */
+ ent_back = 0.0;
+ for ( ih = 0; ih <= it; ih++ )
+ if (norm_histo[ih] != 0)
+ ent_back -= ( norm_histo[ih] / P1[it] ) * log ( norm_histo[ih] / P1[it] );
+
+ /* Entropy of the object pixels */
+ ent_obj = 0.0;
+ for ( ih = it + 1; ih < HISTOGRAM_SIZE; ih++ )
+ if (norm_histo[ih] != 0)
+ ent_obj -= ( norm_histo[ih] / P2[it] ) * log ( norm_histo[ih] / P2[it] );
+
+ /* Total entropy */
+ tot_ent = ent_back + ent_obj;
+
+ if ( max_ent < tot_ent )
+ {
+ max_ent = tot_ent;
+ threshold = it;
+ }
+ }
+
+ if (threshold == INT_MIN)
+ {
+ DBG (5, "sanei_ir_threshold_maxentropy: no threshold found\n");
+ ret = SANE_STATUS_INVAL;
+ }
+ else
+ {
+ ret = SANE_STATUS_GOOD;
+ if (params->depth > 8)
+ {
+ it = 1 << (params->depth - HISTOGRAM_SHIFT);
+ *thresh = threshold * it + it / 2;
+ }
+ else
+ *thresh = threshold;
+ DBG (10, "sanei_ir_threshold_maxentropy: threshold %d\n", *thresh);
+ }
+
+ cleanup:
+ if (P1)
+ free (P1);
+ if (P2)
+ free (P2);
+ return ret;
+}
+
+/* Generate gray scale luminance image from separate R, G, B images
+ */
+SANE_Status
+sanei_ir_RGB_luminance (SANE_Parameters * params, const SANE_Uint **in_img,
+ SANE_Uint **out_img)
+{
+ SANE_Uint *outi;
+ int itop, i;
+
+ if ((params->depth < 8) || (params->depth > 16) ||
+ (params->format != SANE_FRAME_GRAY))
+ {
+ DBG (5, "sanei_ir_RGB_luminance: invalid format\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ itop = params->pixels_per_line * params->lines;
+ outi = malloc (itop * sizeof(SANE_Uint));
+ if (!outi)
+ {
+ DBG (5, "sanei_ir_RGB_luminance: can not allocate out_img\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = itop; i > 0; i--)
+ *(outi++) = (218 * (int) *(in_img[0]++) +
+ 732 * (int) *(in_img[1]++) +
+ 74 * (int) *(in_img[2]++)) >> 10;
+ *out_img = outi;
+ return SANE_STATUS_GOOD;
+}
+
+/* Convert image from >8 bit depth to an 8 bit image
+ */
+SANE_Status
+sanei_ir_to_8bit (SANE_Parameters * params, const SANE_Uint *in_img,
+ SANE_Parameters * out_params, SANE_Uint **out_img)
+{
+ SANE_Uint *outi;
+ size_t ssize;
+ int i, is;
+
+ if ((params->depth < 8) || (params->depth > 16))
+ {
+ DBG (5, "sanei_ir_to_8bit: invalid format\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ ssize = params->pixels_per_line * params->lines;
+ if (params->format == SANE_FRAME_RGB)
+ ssize *= 3;
+ outi = malloc (ssize * sizeof(SANE_Uint));
+ if (!outi)
+ {
+ DBG (5, "sanei_ir_to_8bit: can not allocate out_img\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (out_params)
+ {
+ memmove (out_params, params, sizeof(SANE_Parameters));
+ out_params->bytes_per_line = out_params->pixels_per_line;
+ if (params->format == SANE_FRAME_RGB)
+ out_params->bytes_per_line *= 3;
+ out_params->depth = 8;
+ }
+
+ memmove (outi, in_img, ssize * sizeof(SANE_Uint));
+ is = params->depth - 8;
+ for (i = ssize; i > 0; i--) {
+ *outi = *outi >> is, outi += 2;
+ }
+
+ *out_img = outi;
+ return SANE_STATUS_GOOD;
+}
+
+/* allocate and initialize logarithmic lookup table
+ */
+SANE_Status
+sanei_ir_ln_table (int len, double **lut_ln)
+{
+ double *llut;
+ int i;
+
+ DBG (10, "sanei_ir_ln_table\n");
+
+ llut = malloc (len * sizeof (double));
+ if (!llut)
+ {
+ DBG (5, "sanei_ir_ln_table: no table\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ llut[0] = 0;
+ llut[1] = 0;
+ for (i = 2; i < len; i++)
+ llut[i] = log ((double) i);
+
+ *lut_ln = llut;
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Reduce red spectral overlap from an infrared image plane
+ */
+SANE_Status
+sanei_ir_spectral_clean (const SANE_Parameters * params, double *lut_ln,
+ const SANE_Uint *red_data,
+ SANE_Uint *ir_data)
+{
+ const SANE_Uint *rptr;
+ SANE_Uint *iptr;
+ SANE_Int depth;
+ double *llut;
+ double rval, rsum, rrsum;
+ double risum, rfac, radd;
+ double *norm_histo;
+ int64_t isum;
+ int *calc_buf, *calc_ptr;
+ int ival, imin, imax;
+ int itop, len, ssize;
+ int thresh_low, thresh;
+ int irand, i;
+ SANE_Status status;
+
+ DBG (10, "sanei_ir_spectral_clean\n");
+
+ itop = params->pixels_per_line * params->lines;
+ calc_buf = malloc (itop * sizeof (int)); /* could save this */
+ if (!calc_buf)
+ {
+ DBG (5, "sanei_ir_spectral_clean: no buffer\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ depth = params->depth;
+ len = 1 << depth;
+ if (lut_ln)
+ llut = lut_ln;
+ else
+ {
+ status = sanei_ir_ln_table (len, &llut);
+ if (status != SANE_STATUS_GOOD) {
+ free (calc_buf);
+ return status;
+ }
+ }
+
+ /* determine not transparent areas to exclude them later
+ * TODO: this has not been tested for negatives
+ */
+ thresh_low = INT_MAX;
+ status =
+ sanei_ir_create_norm_histogram (params, ir_data, &norm_histo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (5, "sanei_ir_spectral_clean: no buffer\n");
+ free (calc_buf);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* TODO: remember only needed if cropping is not ok */
+ status = sanei_ir_threshold_maxentropy (params, norm_histo, &thresh);
+ if (status == SANE_STATUS_GOOD)
+ thresh_low = thresh;
+ status = sanei_ir_threshold_otsu (params, norm_histo, &thresh);
+ if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
+ thresh_low = thresh;
+ status = sanei_ir_threshold_yen (params, norm_histo, &thresh);
+ if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
+ thresh_low = thresh;
+ if (thresh_low == INT_MAX)
+ thresh_low = 0;
+ else
+ thresh_low /= 2;
+ DBG (10, "sanei_ir_spectral_clean: low threshold %d\n", thresh_low);
+
+ /* calculate linear regression ired (red) from randomly chosen points */
+ ssize = itop / 2;
+ if (SAMPLE_SIZE < ssize)
+ ssize = SAMPLE_SIZE;
+ isum = 0;
+ rsum = rrsum = risum = 0.0;
+ i = ssize;
+ while (i > 0)
+ {
+ irand = rand () % itop;
+ rval = llut[red_data[irand]];
+ ival = ir_data[irand];
+ if (ival > thresh_low)
+ {
+ isum += ival;
+ rsum += rval;
+ rrsum += rval * rval;
+ risum += rval * (double) ival;
+ i--;
+ }
+ }
+
+ /* "a" in ired = b + a * ln (red) */
+ rfac =
+ ((double) ssize * risum -
+ rsum * (double) isum) / ((double) ssize * rrsum - rsum * rsum);
+ radd = ((double) isum - rfac * rsum) / (double) ssize; /* "b" unused */
+
+ DBG (10, "sanei_ir_spectral_clean: n = %d, ired(red) = %f * ln(red) + %f\n",
+ ssize, rfac, radd);
+
+ /* now calculate ired' = ired - a * ln (red) */
+ imin = INT_MAX;
+ imax = INT_MIN;
+ rptr = red_data;
+ iptr = ir_data;
+ calc_ptr = calc_buf;
+ for (i = itop; i > 0; i--)
+ {
+ ival = *iptr++ - (int) (rfac * llut[*rptr++] + 0.5);
+ if (ival > imax)
+ imax = ival;
+ if (ival < imin)
+ imin = ival;
+ *calc_ptr++ = ival;
+ }
+
+ /* scale the result back into the ired image */
+ calc_ptr = calc_buf;
+ iptr = ir_data;
+ rfac = (double) (len - 1) / (double) (imax - imin);
+ for (i = itop; i > 0; i--)
+ *iptr++ = (double) (*calc_ptr++ - imin) * rfac;
+
+ if (!lut_ln)
+ free (llut);
+ free (calc_buf);
+ free (norm_histo);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Hopefully fast mean filter
+ * JV: what does this do? Remove local mean?
+ */
+SANE_Status
+sanei_ir_filter_mean (const SANE_Parameters * params,
+ const SANE_Uint *in_img, SANE_Uint *out_img,
+ int win_rows, int win_cols)
+{
+ const SANE_Uint *src;
+ SANE_Uint *dest;
+ int num_cols, num_rows;
+ int itop, iadd, isub;
+ int ndiv, the_sum;
+ int nrow, ncol;
+ int hwr, hwc;
+ int *sum;
+ int i, j;
+
+ DBG (10, "sanei_ir_filter_mean, window: %d x%d\n", win_rows, win_cols);
+
+ if (((win_rows & 1) == 0) || ((win_cols & 1) == 0))
+ {
+ DBG (5, "sanei_ir_filter_mean: window even sized\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ num_cols = params->pixels_per_line;
+ num_rows = params->lines;
+
+ sum = malloc (num_cols * sizeof (int));
+ if (!sum)
+ {
+ DBG (5, "sanei_ir_filter_mean: no buffer for sums\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dest = out_img;
+
+ hwr = win_rows / 2; /* half window sizes */
+ hwc = win_cols / 2;
+
+ /* pre-pre calculation */
+ for (j = 0; j < num_cols; j++)
+ {
+ sum[j] = 0;
+ src = in_img + j;
+ for (i = 0; i < hwr; i++)
+ {
+ sum[j] += *src;
+ src += num_cols;
+ }
+ }
+
+ itop = num_rows * num_cols;
+ iadd = hwr * num_cols;
+ isub = (hwr - win_rows) * num_cols;
+ nrow = hwr;
+
+ for (i = 0; i < num_rows; i++)
+ {
+ /* update row sums if possible */
+ if (isub >= 0) /* subtract old row */
+ {
+ nrow--;
+ src = in_img + isub;
+ for (j = 0; j < num_cols; j++)
+ sum[j] -= *src++;
+ }
+ isub += num_cols;
+
+ if (iadd < itop) /* add new row */
+ {
+ nrow++;
+ src = in_img + iadd;
+ for (j = 0; j < num_cols; j++)
+ sum[j] += *src++;
+ }
+ iadd += num_cols;
+
+ /* now we do the image columns using only the precalculated sums */
+
+ the_sum = 0; /* precalculation */
+ for (j = 0; j < hwc; j++)
+ the_sum += sum[j];
+ ncol = hwc;
+
+ /* at the left margin, real index hwc lower */
+ for (j = hwc; j < win_cols; j++)
+ {
+ ncol++;
+ the_sum += sum[j];
+ *dest++ = the_sum / (ncol * nrow);
+ }
+
+ ndiv = ncol * nrow;
+ /* in the middle, real index hwc + 1 higher */
+ for (j = 0; j < num_cols - win_cols; j++)
+ {
+ the_sum -= sum[j];
+ the_sum += sum[j + win_cols];
+ *dest++ = the_sum / ndiv;
+ }
+
+ /* at the right margin, real index hwc + 1 higher */
+ for (j = num_cols - win_cols; j < num_cols - hwc - 1; j++)
+ {
+ ncol--;
+ the_sum -= sum[j]; /* j - hwc - 1 */
+ *dest++ = the_sum / (ncol * nrow);
+ }
+ }
+ free (sum);
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Find noise by adaptive thresholding
+ */
+SANE_Status
+sanei_ir_filter_madmean (const SANE_Parameters * params,
+ const SANE_Uint *in_img,
+ SANE_Uint ** out_img, int win_size,
+ int a_val, int b_val)
+{
+ SANE_Uint *delta_ij, *delta_ptr;
+ SANE_Uint *mad_ij;
+ const SANE_Uint *mad_ptr;
+ SANE_Uint *out_ij, *dest8;
+ double ab_term;
+ int num_rows, num_cols;
+ int threshold, itop;
+ size_t size;
+ int ival, i;
+ int depth;
+ SANE_Status ret = SANE_STATUS_NO_MEM;
+
+ DBG (10, "sanei_ir_filter_madmean\n");
+
+ depth = params->depth;
+ if (depth != 8)
+ {
+ a_val = a_val << (depth - 8);
+ b_val = b_val << (depth - 8);
+ }
+ num_cols = params->pixels_per_line;
+ num_rows = params->lines;
+ itop = num_rows * num_cols;
+ size = itop * sizeof (SANE_Uint);
+ out_ij = malloc (size);
+ delta_ij = malloc (size);
+ mad_ij = malloc (size);
+
+ if (out_ij && delta_ij && mad_ij)
+ {
+ /* get the differences to the local mean */
+ mad_ptr = in_img;
+ if (sanei_ir_filter_mean (params, mad_ptr, delta_ij, win_size, win_size)
+ == SANE_STATUS_GOOD)
+ {
+ delta_ptr = delta_ij;
+ for (i = 0; i < itop; i++)
+ {
+ ival = *mad_ptr++ - *delta_ptr;
+ *delta_ptr++ = abs (ival);
+ }
+ /* make the second filtering window a bit larger */
+ win_size = MAD_WIN2_SIZE(win_size);
+ /* and get the local mean differences */
+ if (sanei_ir_filter_mean
+ (params, delta_ij, mad_ij, win_size,
+ win_size) == SANE_STATUS_GOOD)
+ {
+ mad_ptr = mad_ij;
+ delta_ptr = delta_ij;
+ dest8 = out_ij;
+ /* construct the noise map */
+ ab_term = (b_val - a_val) / (double) b_val;
+ for (i = 0; i < itop; i++)
+ {
+ /* by calculating the threshold */
+ ival = *mad_ptr++;
+ if (ival >= b_val) /* outlier */
+ threshold = a_val;
+ else
+ threshold = a_val + (double) ival *ab_term;
+ /* above threshold is noise, indicated by 0 */
+ if (*delta_ptr++ >= threshold)
+ *dest8++ = 0;
+ else
+ *dest8++ = 255;
+ }
+ *out_img = out_ij;
+ ret = SANE_STATUS_GOOD;
+ }
+ }
+ }
+ else
+ DBG (5, "sanei_ir_filter_madmean: Cannot allocate buffers\n");
+
+ free (mad_ij);
+ free (delta_ij);
+ return ret;
+}
+
+
+/* Add dark pixels to mask from static threshold
+ */
+void
+sanei_ir_add_threshold (const SANE_Parameters * params,
+ const SANE_Uint *in_img,
+ SANE_Uint * mask_img, int threshold)
+{
+ const SANE_Uint *in_ptr;
+ SANE_Uint *mask_ptr;
+ int itop, i;
+
+ DBG (10, "sanei_ir_add_threshold\n");
+
+ itop = params->pixels_per_line * params->lines;
+ in_ptr = in_img;
+ mask_ptr = mask_img;
+
+ for (i = itop; i > 0; i--)
+ {
+ if (*in_ptr++ <= threshold)
+ *mask_ptr = 0;
+ mask_ptr++;
+ }
+}
+
+
+/* Calculate minimal Manhattan distances for an image mask
+ */
+void
+sanei_ir_manhattan_dist (const SANE_Parameters * params,
+ const SANE_Uint * mask_img, unsigned int *dist_map,
+ unsigned int *idx_map, unsigned int erode)
+{
+ const SANE_Uint *mask;
+ unsigned int *index, *manhattan;
+ int rows, cols, itop;
+ int i, j;
+
+ DBG (10, "sanei_ir_manhattan_dist\n");
+
+ if (erode != 0)
+ erode = 255;
+
+ /* initialize maps */
+ cols = params->pixels_per_line;
+ rows = params->lines;
+ itop = rows * cols;
+ mask = mask_img;
+ manhattan = dist_map;
+ index = idx_map;
+ for (i = 0; i < itop; i++)
+ {
+ *manhattan++ = *mask++;
+ *index++ = i;
+ }
+
+ /* traverse from top left to bottom right */
+ manhattan = dist_map;
+ index = idx_map;
+ for (i = 0; i < rows; i++)
+ for (j = 0; j < cols; j++)
+ {
+ if (*manhattan == erode)
+ {
+ /* take original, distance = 0, index stays the same */
+ *manhattan = 0;
+ }
+ else
+ {
+ /* assume maximal distance to clean pixel */
+ *manhattan = cols + rows;
+ /* or one further away than pixel to the top */
+ if (i > 0)
+ if (manhattan[-cols] + 1 < *manhattan)
+ {
+ *manhattan = manhattan[-cols] + 1;
+ *index = index[-cols]; /* index follows */
+ }
+ /* or one further away than pixel to the left */
+ if (j > 0)
+ {
+ if (manhattan[-1] + 1 < *manhattan)
+ {
+ *manhattan = manhattan[-1] + 1;
+ *index = index[-1]; /* index follows */
+ }
+ if (manhattan[-1] + 1 == *manhattan)
+ if (rand () % 2 == 0) /* chose index */
+ *index = index[-1];
+ }
+ }
+ manhattan++;
+ index++;
+ }
+
+ /* traverse from bottom right to top left */
+ manhattan = dist_map + itop - 1;
+ index = idx_map + itop - 1;
+ for (i = rows - 1; i >= 0; i--)
+ for (j = cols - 1; j >= 0; j--)
+ {
+ if (i < rows - 1)
+ {
+ /* either what we had on the first pass
+ or one more than the pixel to the bottm */
+ if (manhattan[+cols] + 1 < *manhattan)
+ {
+ *manhattan = manhattan[+cols] + 1;
+ *index = index[+cols]; /* index follows */
+ }
+ if (manhattan[+cols] + 1 == *manhattan)
+ if (rand () % 2 == 0) /* chose index */
+ *index = index[+cols];
+ }
+ if (j < cols - 1)
+ {
+ /* or one more than pixel to the right */
+ if (manhattan[1] + 1 < *manhattan)
+ {
+ *manhattan = manhattan[1] + 1;
+ *index = index[1]; /* index follows */
+ }
+ if (manhattan[1] + 1 == *manhattan)
+ if (rand () % 2 == 0) /* chose index */
+ *index = index[1];
+ }
+ manhattan--;
+ index--;
+ }
+}
+
+
+/* dilate or erode a mask image */
+
+void
+sanei_ir_dilate (const SANE_Parameters *params, SANE_Uint *mask_img,
+ unsigned int *dist_map, unsigned int *idx_map, int by)
+{
+ SANE_Uint *mask;
+ unsigned int *manhattan;
+ unsigned int erode;
+ unsigned int thresh;
+ int i, itop;
+
+ DBG (10, "sanei_ir_dilate\n");
+
+ if (by == 0)
+ return;
+ if (by > 0)
+ {
+ erode = 0;
+ thresh = by;
+ }
+ else
+ {
+ erode = 1;
+ thresh = -by;
+ }
+
+ itop = params->pixels_per_line * params->lines;
+ mask = mask_img;
+ sanei_ir_manhattan_dist (params, mask_img, dist_map, idx_map, erode);
+
+ manhattan = dist_map;
+ for (i = 0; i < itop; i++)
+ {
+ if (*manhattan++ <= thresh)
+ *mask++ = 0;
+ else
+ *mask++ = 255;
+ }
+
+ return;
+}
+
+
+/* Suggest cropping for dark margins of positive film
+ */
+void
+sanei_ir_find_crop (const SANE_Parameters * params,
+ unsigned int * dist_map, int inner, int * edges)
+{
+ int width = params->pixels_per_line;
+ int height = params->lines;
+ uint64_t sum_x, sum_y, n;
+ int64_t sum_xx, sum_xy;
+ double a, b, mami;
+ unsigned int *src;
+ int off1, off2, inc, wh, i, j;
+
+ DBG (10, "sanei_ir_find_crop\n");
+
+ /* loop through top, bottom, left, right */
+ for (j = 0; j < 4; j++)
+ {
+ if (j < 2) /* top, bottom */
+ {
+ off1 = width / 8; /* only middle 3/4 */
+ off2 = width - off1;
+ n = width - 2 * off1;
+ src = dist_map + off1; /* first row */
+ inc = 1;
+ wh = width;
+ if (j == 1) /* last row */
+ src += (height - 1) * width;
+ }
+ else /* left, right */
+ {
+ off1 = height / 8; /* only middle 3/4 */
+ off2 = height - off1;
+ n = height - 2 * off1;
+ src = dist_map + (off1 * width); /* first column */
+ inc = width;
+ wh = height;
+ if (j == 3)
+ src += width - 1; /* last column */
+ }
+
+ /* calculate linear regression */
+ sum_x = 0; sum_y = 0;
+ sum_xx = 0; sum_xy = 0;
+ for (i = off1; i < off2; i++)
+ {
+ sum_x += i;
+ sum_y += *src;
+ sum_xx += i * i;
+ sum_xy += i * (*src);
+ src += inc;
+ }
+ b = ((double) n * (double) sum_xy - (double) sum_x * (double) sum_y)
+ / ((double) n * (double) sum_xx - (double) sum_x * (double) sum_x);
+ a = ((double) sum_y - b * (double) sum_x) / (double) n;
+
+ DBG (10, "sanei_ir_find_crop: y = %f + %f * x\n", a, b);
+
+ /* take maximal/minimal value from either side */
+ mami = a + b * (wh - 1);
+ if (inner)
+ {
+ if (a > mami)
+ mami = a;
+ }
+ else
+ {
+ if (a < mami)
+ mami = a;
+ }
+ edges[j] = mami + 0.5;
+ }
+ edges[1] = height - edges[1];
+ edges[3] = width - edges[3];
+
+ DBG (10, "sanei_ir_find_crop: would crop at top: %d, bot: %d, left %d, right %d\n",
+ edges[0], edges[1], edges[2], edges[3]);
+
+ return;
+}
+
+
+/* Dilate clean image parts into dirty ones and smooth
+ */
+SANE_Status
+sanei_ir_dilate_mean (const SANE_Parameters * params,
+ SANE_Uint **in_img,
+ SANE_Uint * mask_img,
+ int dist_max, int expand, int win_size,
+ SANE_Bool smooth, int inner,
+ int *crop)
+{
+ SANE_Uint *color;
+ SANE_Uint *plane;
+ unsigned int *dist_map, *manhattan;
+ unsigned int *idx_map, *index;
+ int dist;
+ int rows, cols;
+ int k, i, itop;
+ SANE_Status ret = SANE_STATUS_NO_MEM;
+
+ DBG (10, "sanei_ir_dilate_mean(): dist max = %d, expand = %d, win size = %d, smooth = %d, inner = %d\n",
+ dist_max, expand, win_size, smooth, inner);
+
+ cols = params->pixels_per_line;
+ rows = params->lines;
+ itop = rows * cols;
+ idx_map = malloc (itop * sizeof (unsigned int));
+ dist_map = malloc (itop * sizeof (unsigned int));
+ plane = malloc (itop * sizeof (SANE_Uint));
+
+ if (!idx_map || !dist_map || !plane)
+ DBG (5, "sanei_ir_dilate_mean: Cannot allocate buffers\n");
+ else
+ {
+ /* expand dirty regions into their half dirty surround*/
+ if (expand > 0)
+ sanei_ir_dilate (params, mask_img, dist_map, idx_map, expand);
+ /* for dirty pixels determine the closest clean ones */
+ sanei_ir_manhattan_dist (params, mask_img, dist_map, idx_map, 1);
+
+ /* use the distance map to find how to crop dark edges */
+ if (crop)
+ sanei_ir_find_crop (params, dist_map, inner, crop);
+
+ /* replace dirty pixels */
+ for (k = 0; k < 3; k++)
+ {
+ manhattan = dist_map;
+ index = idx_map;
+ color = in_img[k];
+ /* first replacement */
+ for (i = 0; i < itop; i++)
+ {
+ dist = *manhattan++;
+ if ((dist != 0) && (dist <= dist_max))
+ color[i] = color[index[i]];
+ }
+ /* adapt pixels to their new surround and
+ * smooth the whole image or the replaced pixels only */
+ ret =
+ sanei_ir_filter_mean (params, color, plane, win_size, win_size);
+ if (ret != SANE_STATUS_GOOD)
+ break;
+ else
+ if (smooth)
+ {
+ /* a second mean results in triangular blur */
+ DBG (10, "sanei_ir_dilate_mean(): smoothing whole image\n");
+ ret =
+ sanei_ir_filter_mean (params, plane, color, win_size,
+ win_size);
+ if (ret != SANE_STATUS_GOOD)
+ break;
+ }
+ else
+ {
+ /* replace with smoothened pixels only */
+ DBG (10, "sanei_ir_dilate_mean(): smoothing replaced pixels only\n");
+ manhattan = dist_map;
+ for (i = 0; i < itop; i++)
+ {
+ dist = *manhattan++;
+ if ((dist != 0) && (dist <= dist_max))
+ color[i] = plane[i];
+ }
+ }
+ }
+ }
+ free (plane);
+ free (dist_map);
+ free (idx_map);
+
+ return ret;
+}
diff --git a/sanei/sanei_pa4s2.c b/sanei/sanei_pa4s2.c
index 8fe4260..c15dd5a 100644
--- a/sanei/sanei_pa4s2.c
+++ b/sanei/sanei_pa4s2.c
@@ -72,7 +72,10 @@
# if defined (__ICC) && __ICC >= 700
# define __GNUC__ 2
# endif
-# include <sys/io.h>
+# include <sys/io.h>
+# ifndef SANE_HAVE_SYS_IO_H_WITH_INB_OUTB
+# define IO_SUPPORT_MISSING
+# endif
# if defined (__ICC) && __ICC >= 700
# undef __GNUC__
# elif defined(__ICC) && defined(HAVE_ASM_IO_H)
diff --git a/sanei/sanei_pio.c b/sanei/sanei_pio.c
index ef00861..00e40ee 100644
--- a/sanei/sanei_pio.c
+++ b/sanei/sanei_pio.c
@@ -61,6 +61,9 @@
#ifdef HAVE_SYS_IO_H
# include <sys/io.h> /* use where available (glibc 2.x, for example) */
+# ifndef SANE_HAVE_SYS_IO_H_WITH_INB_OUTB
+# define IO_SUPPORT_MISSING
+# endif
#elif HAVE_ASM_IO_H
# include <asm/io.h> /* ugly, but backwards compatible */
#elif HAVE_SYS_HW_H
diff --git a/sanei/sanei_pp.c b/sanei/sanei_pp.c
index ced1a85..6146d1e 100644
--- a/sanei/sanei_pp.c
+++ b/sanei/sanei_pp.c
@@ -94,6 +94,9 @@
# define __GNUC__ 2
# endif
# include <sys/io.h>
+# ifndef SANE_HAVE_SYS_IO_H_WITH_INB_OUTB
+# define IO_SUPPORT_MISSING
+# endif
# if defined (__ICC) && __ICC >= 700
# undef __GNUC__
# elif defined(__ICC) && defined(HAVE_ASM_IO_H)
diff --git a/sanei/sanei_thread.c b/sanei/sanei_thread.c
index fd58af2..8db4a21 100644
--- a/sanei/sanei_thread.c
+++ b/sanei/sanei_thread.c
@@ -512,11 +512,15 @@ sanei_thread_waitpid( SANE_Pid pid, int *status )
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 ( EDEADLK == rc ) {
+ if ( (pthread_t)pid != pthread_self() ) {
+ /* 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;
diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c
index 7401658..f210d4f 100644
--- a/sanei/sanei_usb.c
+++ b/sanei/sanei_usb.c
@@ -154,6 +154,7 @@ typedef struct
SANE_Int control_in_ep;
SANE_Int control_out_ep;
SANE_Int interface_nr;
+ SANE_Int alt_setting;
SANE_Int missing;
#ifdef HAVE_LIBUSB
usb_dev_handle *libusb_handle;
@@ -460,8 +461,6 @@ sanei_libusb_strerror (int errcode)
default:
return "Unknown libusb-1.0 error code";
}
-
- return "Unknown libusb-1.0 error code";
}
#endif /* HAVE_LIBUSB_1_0 */
@@ -635,6 +634,7 @@ static void usbcall_scan_devices(void)
device.product = pDevDesc->idProduct;
device.method = sanei_usb_method_usbcalls;
device.interface_nr = interface;
+ device.alt_setting = 0;
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);
@@ -821,7 +821,7 @@ static void libusb_scan_devices(void)
"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].num_altsetting != 0
? dev->config[0].interface[interface].altsetting[0].
bInterfaceClass : -1);
}
@@ -845,6 +845,7 @@ static void libusb_scan_devices(void)
device.product = dev->descriptor.idProduct;
device.method = sanei_usb_method_libusb;
device.interface_nr = interface;
+ device.alt_setting = 0;
DBG (4,
"%s: found libusb device (0x%04x/0x%04x) interface "
"%d at %s\n", __func__,
@@ -991,7 +992,7 @@ static void libusb_scan_devices(void)
"%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].num_altsetting != 0)
? config0->interface[interface].altsetting[0].bInterfaceClass : -1);
}
@@ -1018,6 +1019,7 @@ static void libusb_scan_devices(void)
device.product = pid;
device.method = sanei_usb_method_libusb;
device.interface_nr = interface;
+ device.alt_setting = 0;
DBG (4,
"%s: found libusb-1.0 device (0x%04x/0x%04x) interface "
"%d at %s\n", __func__,
@@ -1381,30 +1383,32 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn)
"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)
+ result = usb_set_configuration (devices[devcount].libusb_handle,
+ dev->config[0].bConfigurationValue);
+ if (result < 0)
{
- usb_close (devices[devcount].libusb_handle);
- return status;
+ 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;
+ }
}
}
@@ -1445,13 +1449,13 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn)
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;
- }
+ /* 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];
@@ -1672,37 +1676,40 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn)
"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);
+ result = 0;
+ if (config != config0->bConfigurationValue)
+ result = libusb_set_configuration (devices[devcount].lu_handle,
+ config0->bConfigurationValue);
- 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)
+ if (result < 0)
{
- DBG (3, "Maybe the kernel scanner driver or usblp claims the "
- "interface? Ignoring this error...\n");
- status = SANE_STATUS_GOOD;
- }
+ SANE_Status status = SANE_STATUS_INVAL;
- if (status != SANE_STATUS_GOOD)
- {
- libusb_close (devices[devcount].lu_handle);
- return status;
+ 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);
+ libusb_free_config_descriptor (config0);
+ return status;
+ }
}
}
+ libusb_free_config_descriptor (config0);
/* Claim the interface */
result = libusb_claim_interface (devices[devcount].lu_handle,
@@ -2128,22 +2135,24 @@ sanei_usb_close (SANE_Int dn)
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 */
+ /* This call seems to be required by Linux xhci driver
+ * even though it should be a no-op. Without it, the
+ * host or driver does not reset it's data toggle bit.
+ * We intentionally ignore the return val */
+ sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
+
usb_release_interface (devices[dn].libusb_handle,
devices[dn].interface_nr);
usb_close (devices[dn].libusb_handle);
}
#elif defined(HAVE_LIBUSB_1_0)
{
+ /* This call seems to be required by Linux xhci driver
+ * even though it should be a no-op. Without it, the
+ * host or driver does not reset it's data toggle bit.
+ * We intentionally ignore the return val */
+ sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
+
libusb_release_interface (devices[dn].lu_handle,
devices[dn].interface_nr);
libusb_close (devices[dn].lu_handle);
@@ -2168,7 +2177,6 @@ sanei_usb_set_timeout (SANE_Int timeout)
SANE_Status
sanei_usb_clear_halt (SANE_Int dn)
{
-#ifdef HAVE_LIBUSB
int ret;
if (dn >= device_number || dn < 0)
@@ -2177,6 +2185,14 @@ sanei_usb_clear_halt (SANE_Int dn)
return SANE_STATUS_INVAL;
}
+#ifdef HAVE_LIBUSB
+
+ /* This call seems to be required by Linux xhci driver
+ * even though it should be a no-op. Without it, the
+ * host or driver does not send the clear to the device.
+ * We intentionally ignore the return val */
+ sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
+
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);
@@ -2189,18 +2205,13 @@ sanei_usb_clear_halt (SANE_Int dn)
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;
- }
+ /* This call seems to be required by Linux xhci driver
+ * even though it should be a no-op. Without it, the
+ * host or driver does not send the clear to the device.
+ * We intentionally ignore the return val */
+ sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
if (ret){
@@ -3038,6 +3049,8 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate)
DBG (5, "sanei_usb_set_altinterface: alternate = %d\n", alternate);
+ devices[dn].alt_setting = alternate;
+
if (devices[dn].method == sanei_usb_method_scanner_driver)
{
#if defined(__linux__)