diff options
Diffstat (limited to 'sanei')
| -rw-r--r-- | sanei/Makefile.am | 2 | ||||
| -rw-r--r-- | sanei/Makefile.in | 17 | ||||
| -rw-r--r-- | sanei/sanei_ab306.c | 3 | ||||
| -rw-r--r-- | sanei/sanei_ir.c | 1205 | ||||
| -rw-r--r-- | sanei/sanei_pa4s2.c | 5 | ||||
| -rw-r--r-- | sanei/sanei_pio.c | 3 | ||||
| -rw-r--r-- | sanei/sanei_pp.c | 3 | ||||
| -rw-r--r-- | sanei/sanei_thread.c | 14 | ||||
| -rw-r--r-- | sanei/sanei_usb.c | 171 | 
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__)  | 
