summaryrefslogtreecommitdiff
path: root/backend/artec_eplus48u.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/artec_eplus48u.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/artec_eplus48u.c')
-rw-r--r--backend/artec_eplus48u.c4568
1 files changed, 4568 insertions, 0 deletions
diff --git a/backend/artec_eplus48u.c b/backend/artec_eplus48u.c
new file mode 100644
index 0000000..c26fbb0
--- /dev/null
+++ b/backend/artec_eplus48u.c
@@ -0,0 +1,4568 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2002 Michael Herder <crapsite@gmx.net>
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+This backend is based on the "gt68xxtest" program written by the following
+persons:
+ Sergey Vlasov <vsu@mivlgu.murom.ru>
+ - Main backend code.
+
+ Andreas Nowack <nowack.andreas@gmx.de>
+ - Support for GT6801 (Mustek ScanExpress 1200 UB Plus).
+
+ David Stevenson <david.stevenson@zoom.co.uk>
+ - Automatic AFE gain and offset setting.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+Please note:
+The calibration code from the gt68xxtest program isn't used here, since I
+couldn't get it working. I'm using my own calibration code, which is based
+on wild assumptions based on the USB logs from the windoze driver.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+It also contains code from the plustek backend
+
+Copyright (C) 2000-2002 Gerhard Jaeger <g.jaeger@earthling.net>
+
+and from the mustek_usb backend
+
+Copyright (C) 2000 Mustek.
+Maintained by Tom Wang <tom.wang@mustek.com.tw>
+Updates (C) 2001 by Henning Meier-Geinitz.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice. */
+
+#define BUILD 11
+
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME artec_eplus48u
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include "artec_eplus48u.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define _DEFAULT_DEVICE "/dev/usbscanner"
+#define ARTEC48U_CONFIG_FILE "artec_eplus48u.conf"
+
+#define _SHADING_FILE_BLACK "artec48ushading_black"
+#define _SHADING_FILE_WHITE "artec48ushading_white"
+#define _EXPOSURE_FILE "artec48uexposure"
+#define _OFFSET_FILE "artec48uoffset"
+
+#define _BYTE 3
+#define _STRING 2
+#define _FLOAT 1
+#define _INT 0
+
+/*for calibration*/
+#define WHITE_MIN 243*257
+#define WHITE_MAX 253*257
+#define BLACK_MIN 8*257
+#define BLACK_MAX 18*257
+#define EXPOSURE_STEP 280
+
+static Artec48U_Device *first_dev = 0;
+static Artec48U_Scanner *first_handle = 0;
+static SANE_Int num_devices = 0;
+static char devName[PATH_MAX];
+static char firmwarePath[PATH_MAX];
+static char vendor_string[PATH_MAX];
+static char model_string[PATH_MAX];
+
+static SANE_Bool cancelRead;
+static int isEPro;
+static int eProMult;
+static SANE_Auth_Callback auth = NULL;
+static double gamma_master_default = 1.7;
+static double gamma_r_default = 1.0;
+static double gamma_g_default = 1.0;
+static double gamma_b_default = 1.0;
+
+static SANE_Word memory_read_value = 0x200c; /**< Memory read - wValue */
+static SANE_Word memory_write_value = 0x200b; /**< Memory write - wValue */
+static SANE_Word send_cmd_value = 0x2010; /**< Send normal command - wValue */
+static SANE_Word send_cmd_index = 0x3f40; /**< Send normal command - wIndex */
+static SANE_Word recv_res_value = 0x2011; /**< Receive normal result - wValue */
+static SANE_Word recv_res_index = 0x3f00; /**< Receive normal result - wIndex */
+static SANE_Word send_small_cmd_value = 0x2012; /**< Send small command - wValue */
+static SANE_Word send_small_cmd_index = 0x3f40; /**< Send small command - wIndex */
+static SANE_Word recv_small_res_value = 0x2013; /**< Receive small result - wValue */
+static SANE_Word recv_small_res_index = 0x3f00; /**< Receive small result - wIndex */
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+static SANE_Word resbit_list[] = {
+ 6,
+ 50, 100, 200, 300, 600, 1200
+};
+
+static SANE_Range brightness_contrast_range = {
+ -127,
+ 127,
+ 0
+};
+
+static SANE_Range blacklevel_range = {
+ 20,
+ 240,
+ 1
+};
+
+static SANE_Range gamma_range = {
+ 0, /* minimum */
+ SANE_FIX (4.0), /* maximum */
+ 0 /* quantization */
+};
+
+static SANE_Range scan_range_x = {
+ 0, /* minimum */
+ SANE_FIX (216.0), /* maximum */
+ 0 /* quantization */
+};
+
+static SANE_Range scan_range_y = {
+ 0, /* minimum */
+ SANE_FIX (297.0), /* maximum */
+ 0 /* quantization */
+};
+
+
+static SANE_Word bitdepth_list[] = {
+ 2, 8, 16
+};
+
+static SANE_Word bitdepth_list2[] = {
+ 1, 8
+};
+
+static Artec48U_Exposure_Parameters exp_params;
+static Artec48U_Exposure_Parameters default_exp_params =
+ { 0x009f, 0x0109, 0x00cb };
+static Artec48U_AFE_Parameters afe_params;
+static Artec48U_AFE_Parameters default_afe_params =
+ { 0x28, 0x0a, 0x2e, 0x03, 0x2e, 0x03 };
+
+static SANE_Status
+download_firmware_file (Artec48U_Device * chip)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Byte *buf = NULL;
+ int size = -1;
+ FILE *f;
+
+ XDBG ((2, "Try to open firmware file: \"%s\"\n", chip->firmware_path));
+ f = fopen (chip->firmware_path, "rb");
+ if (!f)
+ {
+ XDBG ((2, "Cannot open firmware file \"%s\"\n", firmwarePath));
+ status = SANE_STATUS_INVAL;
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ fseek (f, 0, SEEK_END);
+ size = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ if (size == -1)
+ {
+ XDBG ((2, "Error getting size of firmware file \"%s\"\n",
+ chip->firmware_path));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "firmware size: %d\n", size));
+ buf = (SANE_Byte *) malloc (size);
+ if (!buf)
+ {
+ XDBG ((2, "Cannot allocate %d bytes for firmware\n", size));
+ status = SANE_STATUS_NO_MEM;
+ }
+ }
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ int bytes_read = fread (buf, 1, size, f);
+ if (bytes_read != size)
+ {
+ XDBG ((2, "Problem reading firmware file \"%s\"\n",
+ chip->firmware_path));
+ status = SANE_STATUS_INVAL;
+ }
+ }
+
+ if (f)
+ fclose (f);
+
+ if (status == SANE_STATUS_GOOD)
+ {
+ status = artec48u_download_firmware (chip, buf, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "Firmware download failed\n"));
+ }
+ }
+
+ if (buf)
+ free (buf);
+ return status;
+}
+
+static SANE_Status
+init_calibrator (Artec48U_Scanner * s)
+{
+ XDBG ((2, "Init calibrator size %d\n",30720 * s->dev->epro_mult));
+ s->shading_buffer_w = (unsigned char *) malloc (30720 * s->dev->epro_mult); /*epro*/
+ s->shading_buffer_b = (unsigned char *) malloc (30720 * s->dev->epro_mult); /*epro*/
+ s->shading_buffer_white[0] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof(unsigned int));/*epro*/
+ s->shading_buffer_black[0] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_white[1] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_black[1] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_white[2] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+ s->shading_buffer_black[2] =
+ (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/
+
+ if (!s->shading_buffer_w || !s->shading_buffer_b
+ || !s->shading_buffer_white[0] || !s->shading_buffer_black[0]
+ || !s->shading_buffer_white[1] || !s->shading_buffer_black[1]
+ || !s->shading_buffer_white[2] || !s->shading_buffer_black[2])
+ {
+ if (s->shading_buffer_w)
+ free (s->shading_buffer_w);
+ if (s->shading_buffer_b)
+ free (s->shading_buffer_b);
+ if (s->shading_buffer_white[0])
+ free (s->shading_buffer_white[0]);
+ if (s->shading_buffer_black[0])
+ free (s->shading_buffer_black[0]);
+ if (s->shading_buffer_white[1])
+ free (s->shading_buffer_white[1]);
+ if (s->shading_buffer_black[1])
+ free (s->shading_buffer_black[1]);
+ if (s->shading_buffer_white[2])
+ free (s->shading_buffer_white[2]);
+ if (s->shading_buffer_black[2])
+ free (s->shading_buffer_black[2]);
+ return SANE_STATUS_NO_MEM;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static void
+init_shading_buffer (Artec48U_Scanner * s)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ s->temp_shading_buffer[j][i] = 0;
+ }
+ }
+}
+
+static void
+add_to_shading_buffer (Artec48U_Scanner * s, unsigned int **buffer_pointers)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ s->temp_shading_buffer[j][i] += buffer_pointers[j][i];
+ }
+ }
+}
+
+static void
+finish_shading_buffer (Artec48U_Scanner * s, SANE_Bool white)
+{
+ unsigned int i, j, cnt, c, div;
+ unsigned long max_r;
+ unsigned long max_g;
+ unsigned long max_b;
+ unsigned char *shading_buffer;
+ cnt = 0;
+
+ if (white)
+ {
+ shading_buffer = s->shading_buffer_w;
+ div = s->dev->shading_lines_w;
+ }
+ else
+ {
+ shading_buffer = s->shading_buffer_b;
+ div = s->dev->shading_lines_b;
+ }
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int value = s->temp_shading_buffer[j][i] / (div);
+ shading_buffer[cnt] = (SANE_Byte) (value & 0xff);
+ ++cnt;
+ shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff);
+ ++cnt;
+ }
+ }
+ max_r = 0;
+ max_g = 0;
+ max_b = 0;
+
+ for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/
+ {
+ i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8);
+ max_r += i;
+ i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8);
+ max_g += i;
+ i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8);
+ max_b += i;
+ }
+}
+
+static void
+finish_exposure_buffer (Artec48U_Scanner * s, int *avg_r, int *avg_g,
+ int *avg_b)
+{
+ unsigned int i, j, cnt, c, div;
+ unsigned int max_r;
+ unsigned int max_g;
+ unsigned int max_b;
+ unsigned char *shading_buffer;
+ cnt = 0;
+
+ shading_buffer = s->shading_buffer_w;
+ div = s->dev->shading_lines_w;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int value = s->temp_shading_buffer[j][i] / (div);
+ shading_buffer[cnt] = (SANE_Byte) (value & 0xff);
+ ++cnt;
+ shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff);
+ ++cnt;
+ }
+ }
+ max_r = 0;
+ max_g = 0;
+ max_b = 0;
+ for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/
+ {
+ i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8);
+ if (i > max_r)
+ max_r = i;
+ i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8);
+ if (i > max_g)
+ max_g = i;
+ i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8);
+ if (i > max_b)
+ max_b = i;
+ }
+ *avg_r = max_r;
+ *avg_g = max_g;
+ *avg_b = max_b;
+}
+
+static void
+finish_offset_buffer (Artec48U_Scanner * s, int *avg_r, int *avg_g,
+ int *avg_b)
+{
+ unsigned int i, j, cnt, c, div;
+ unsigned int min_r;
+ unsigned int min_g;
+ unsigned int min_b;
+ unsigned char *shading_buffer;
+ cnt = 0;
+
+ shading_buffer = s->shading_buffer_b;
+ div = s->dev->shading_lines_b;
+
+ for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int value = s->temp_shading_buffer[j][i] / (div);
+ shading_buffer[cnt] = (SANE_Byte) (value & 0xff);
+ ++cnt;
+ shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff);
+ ++cnt;
+ }
+ }
+ min_r = 65535;
+ min_g = 65535;
+ min_b = 65535;
+ for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/
+ {
+ i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8);
+ if (i < min_r)
+ min_r = i;
+ i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8);
+ if (i < min_g)
+ min_g = i;
+ i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8);
+ if (i < min_b)
+ min_b = i;
+ }
+ *avg_r = min_r;
+ *avg_g = min_g;
+ *avg_b = min_b;
+}
+
+static SANE_Status
+artec48u_wait_for_positioning (Artec48U_Device * chip)
+{
+ SANE_Status status;
+ SANE_Bool moving;
+
+ while (SANE_TRUE)
+ {
+ status = artec48u_is_moving (chip, &moving);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (!moving)
+ break;
+ usleep (100000);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+copy_scan_line (Artec48U_Scanner * s)
+{
+ /*For resolution of 1200 dpi we have to interpolate
+ horizontally, because the optical horizontal resolution is
+ limited to 600 dpi. We simply use the avarage value of two pixels. */
+ int cnt, i, j;
+ int xs = s->params.pixel_xs;
+ int interpolate = 0;
+ int value;
+ int value1;
+ int value2;
+ if ((s->reader->params.ydpi == 1200) && (s->dev->is_epro == 0)) /*epro*/
+ interpolate = 1;
+ cnt = 0;
+ if (s->params.color)
+ {
+ if (s->params.depth > 8)
+ {
+ for (i = xs - 1; i >= 0; i--)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ value = s->buffer_pointers[j][i];
+ s->line_buffer[cnt] = LOBYTE (value);
+ ++cnt;
+ s->line_buffer[cnt] = HIBYTE (value);
+ ++cnt;
+ }
+ if (interpolate == 1) /*1200 dpi */
+ cnt += 6;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 12) - 12; i += 12)
+ {
+ value1 = (int) s->line_buffer[i];
+ value1 += (int) (s->line_buffer[i + 1] << 8);
+ value2 = (int) s->line_buffer[i + 12];
+ value2 += (int) (s->line_buffer[i + 13] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 6] = LOBYTE (value);
+ s->line_buffer[i + 7] = HIBYTE (value);
+
+ value1 = (int) s->line_buffer[i + 2];
+ value1 += (int) (s->line_buffer[i + 3] << 8);
+ value2 = (int) s->line_buffer[i + 14];
+ value2 += (int) (s->line_buffer[i + 15] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 8] = LOBYTE (value);
+ s->line_buffer[i + 9] = HIBYTE (value);
+
+ value1 = (int) s->line_buffer[i + 4];
+ value1 += (int) (s->line_buffer[i + 5] << 8);
+ value2 = (int) s->line_buffer[i + 16];
+ value2 += (int) (s->line_buffer[i + 17] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 10] = LOBYTE (value);
+ s->line_buffer[i + 11] = HIBYTE (value);
+ }
+ }
+ }
+ else
+ {
+ for (i = xs - 1; i >= 0; i--)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ value = s->buffer_pointers[j][i];
+ s->line_buffer[cnt] = (SANE_Byte) (value / 257);
+ cnt += 1;
+ }
+ if (interpolate == 1) /*1200 dpi */
+ cnt += 3;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 6) - 6; i += 6)
+ {
+ value1 = (int) s->line_buffer[i];
+ value2 = (int) s->line_buffer[i + 6];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 3] = (SANE_Byte) (value);
+
+ value1 = (int) s->line_buffer[i + 1];
+ value2 = (int) s->line_buffer[i + 7];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 4] = (SANE_Byte) (value);
+
+ value1 = (int) s->line_buffer[i + 2];
+ value2 = (int) s->line_buffer[i + 8];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 5] = (SANE_Byte) (value);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (s->params.depth > 8)
+ {
+ for (i = xs - 1; i >= 0; --i)
+ {
+ value = s->buffer_pointers[0][i];
+ s->line_buffer[cnt] = LOBYTE (value);
+ ++cnt;
+ s->line_buffer[cnt] = HIBYTE (value);
+ ++cnt;
+ if (interpolate == 1) /*1200 dpi */
+ cnt += 2;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 4) - 4; i += 4)
+ {
+ value1 = (int) s->line_buffer[i];
+ value1 += (int) (s->line_buffer[i + 1] << 8);
+ value2 = (int) s->line_buffer[i + 4];
+ value2 += (int) (s->line_buffer[i + 5] << 8);
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 65535)
+ value = 65535;
+ s->line_buffer[i + 2] = LOBYTE (value);
+ s->line_buffer[i + 3] = HIBYTE (value);
+ }
+ }
+ }
+ else
+ {
+ if (s->params.lineart == SANE_FALSE)
+ {
+ for (i = xs - 1; i >= 0; --i)
+ {
+ value = s->buffer_pointers[0][i];
+ s->line_buffer[cnt] = (SANE_Byte) (value / 257);
+ ++cnt;
+ if (interpolate == 1) /*1200 dpi */
+ ++cnt;
+ }
+ if (interpolate == 1)
+ {
+ for (i = 0; i < (xs * 2) - 2; i += 2)
+ {
+ value1 = (int) s->line_buffer[i];
+ value2 = (int) s->line_buffer[i + 2];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->line_buffer[i + 1] = (SANE_Byte) (value);
+ }
+ }
+ }
+ else
+ {
+ int cnt2;
+ int bit_cnt = 0;
+ int black_level = s->val[OPT_BLACK_LEVEL].w;
+ /*copy to lineart_buffer */
+ for (i = xs - 1; i >= 0; --i)
+ {
+ s->lineart_buffer[cnt] =
+ (SANE_Byte) (s->buffer_pointers[0][i] / 257);
+ ++cnt;
+ if (interpolate == 1) /*1200 dpi */
+ ++cnt;
+ }
+ cnt2 = cnt - 1;
+ cnt = 0;
+ if (interpolate == 1)
+ {
+ for (i = 0; i < cnt2 - 2; i += 2)
+ {
+ value1 = (int) s->lineart_buffer[i];
+ value2 = (int) s->lineart_buffer[i + 2];
+ value = (value1 + value2) / 2;
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ s->lineart_buffer[i + 1] = (SANE_Byte) (value);
+ }
+ }
+ /* in this case, every value in buffer_pointers represents a bit */
+ for (i = 0; i < cnt2; i++)
+ {
+ SANE_Byte temp;
+ if (bit_cnt == 0)
+ s->line_buffer[cnt] = 0; /*clear */
+ temp = s->lineart_buffer[i];
+ if (temp <= black_level)
+ s->line_buffer[cnt] |= 1 << (7 - bit_cnt);
+ ++bit_cnt;
+ if (bit_cnt > 7)
+ {
+ bit_cnt = 0;
+ ++cnt;
+ }
+ }
+
+ }
+ }
+ }
+}
+
+/*.............................................................................
+ * attach a device to the backend
+ */
+static SANE_Status
+attach (const char *dev_name, Artec48U_Device ** devp)
+{
+ SANE_Status status;
+ Artec48U_Device *dev;
+
+ XDBG ((1, "attach (%s, %p)\n", dev_name, (void *) devp));
+
+ if (!dev_name)
+ {
+ XDBG ((1, "attach: devname == NULL\n"));
+ return SANE_STATUS_INVAL;
+ }
+ /* already attached ? */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (0 == strcmp (dev->name, dev_name))
+ {
+ if (devp)
+ *devp = dev;
+ XDBG ((3, "attach: device %s already attached\n", dev_name));
+ return SANE_STATUS_GOOD;
+ }
+ }
+ XDBG ((3, "attach: device %s NOT attached\n", dev_name));
+ /* allocate some memory for the device */
+ artec48u_device_new (&dev);
+ if (NULL == dev)
+ return SANE_STATUS_NO_MEM;
+
+ dev->fd = -1;
+ dev->name = strdup (dev_name);
+ dev->sane.name = strdup (dev_name);
+/*
+ * go ahead and open the scanner device
+ */
+ status = artec48u_device_open (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "Could not open device!!\n"));
+ artec48u_device_free (dev);
+ return status;
+ }
+ /*limit the size of vendor and model string to 40 */
+ vendor_string[40] = 0;
+ model_string[40] = 0;
+
+ /* assign all the stuff we need fo this device... */
+ dev->sane.vendor = strdup (vendor_string);
+ XDBG ((3, "attach: setting vendor string: %s\n", vendor_string));
+ dev->sane.model = strdup (model_string);
+ XDBG ((3, "attach: setting model string: %s\n", model_string));
+ dev->sane.type = "flatbed scanner";
+ dev->firmware_path = strdup (firmwarePath);
+
+ dev->epro_mult = eProMult;
+ dev->is_epro = isEPro;
+ XDBG ((1, "attach eProMult %d\n", eProMult));
+ XDBG ((1, "attach isEPro %d\n", isEPro));
+ dev->optical_xdpi = 600 * dev->epro_mult; /*epro*/
+ dev->optical_ydpi = 1200 * dev->epro_mult; /*epro*/
+ dev->base_ydpi = 600 * dev->epro_mult; /*epro*/
+ dev->xdpi_offset = 0; /* in optical_xdpi units */
+ dev->ydpi_offset = 280 * dev->epro_mult; /* in optical_ydpi units */
+ dev->x_size = 5120 * dev->epro_mult; /*epro*/ /* in optical_xdpi units */
+ dev->y_size = 14100 * dev->epro_mult; /*epro*/ /* in optical_ydpi units */
+ dev->shading_offset = 10 * dev->epro_mult;
+ dev->shading_lines_b = 70 * dev->epro_mult;
+ dev->shading_lines_w = 70 * dev->epro_mult;
+
+ dev->gamma_master = gamma_master_default;
+ dev->gamma_r = gamma_r_default;
+ dev->gamma_g = gamma_g_default;
+ dev->gamma_b = gamma_b_default;
+
+ dev->afe_params.r_offset = afe_params.r_offset;
+ dev->afe_params.g_offset = afe_params.g_offset;
+ dev->afe_params.b_offset = afe_params.b_offset;
+
+ dev->afe_params.r_pga = default_afe_params.r_pga;
+ dev->afe_params.g_pga = default_afe_params.g_pga;
+ dev->afe_params.b_pga = default_afe_params.b_pga;
+
+ dev->exp_params.r_time = exp_params.r_time;
+ dev->exp_params.g_time = exp_params.g_time;
+ dev->exp_params.b_time = exp_params.b_time;
+
+
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ *devp = first_dev;
+ status = artec48u_device_close (dev);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ Artec48U_Device *dev;
+ SANE_Status status;
+
+ status = attach (devname, &dev);
+ if (SANE_STATUS_GOOD != status)
+ return status;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * function to decode an value and give it back to the caller.
+ * @param src - pointer to the source string to check
+ * @param opt - string that keeps the option name to check src for
+ * @param what - _FLOAT or _INT
+ * @param result - pointer to the var that should receive our result
+ * @param def - default value that result should be in case of any error
+ * @return The function returns SANE_TRUE if the option has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+decodeVal (char *src, char *opt, int what, void *result, void *def)
+{
+ char *tmp, *tmp2;
+ const char *name;
+
+/* skip the option string */
+ name = (const char *) &src[strlen ("option")];
+
+/* get the name of the option */
+ name = sanei_config_get_string (name, &tmp);
+
+ if (tmp)
+ {
+ /* on success, compare wiht the given one */
+ if (0 == strcmp (tmp, opt))
+ {
+ XDBG ((1, "Decoding option >%s<\n", opt));
+ if (_INT == what)
+ {
+ /* assign the default value for this option... */
+ *((int *) result) = *((int *) def);
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ *((int *) result) = strtol (tmp2, 0, 0);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ else if (_FLOAT == what)
+ {
+ /* assign the default value for this option... */
+ *((double *) result) = *((double *) def);
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ *((double *) result) = strtod (tmp2, 0);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ else if (_BYTE == what)
+ {
+ /* assign the default value for this option... */
+ *((SANE_Byte *) result) = *((SANE_Byte *) def);
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ name = sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ *((SANE_Byte *) result) =
+ (SANE_Byte) strtol (tmp2, 0, 0);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ else if (_STRING == what)
+ {
+ if (*name)
+ {
+ /* get the configuration value and decode it */
+ sanei_config_get_string (name, &tmp2);
+ if (tmp2)
+ {
+ strcpy ((char *) result, (char *) tmp2);
+ free (tmp2);
+ }
+ }
+ free (tmp);
+ return SANE_TRUE;
+ }
+ }
+ free (tmp);
+ }
+ return SANE_FALSE;
+}
+
+/**
+ * function to retrive the device name of a given string
+ * @param src - string that keeps the option name to check src for
+ * @param dest - pointer to the string, that should receive the detected
+ * devicename
+ * @return The function returns SANE_TRUE if the devicename has been found,
+ * if not, it returns SANE_FALSE
+ */
+static SANE_Bool
+decodeDevName (char *src, char *dest)
+{
+ char *tmp;
+ const char *name;
+
+ if (0 == strncmp ("device", src, 6))
+ {
+ name = (const char *) &src[strlen ("device")];
+ name = sanei_config_skip_whitespace (name);
+
+ XDBG ((1, "Decoding device name >%s<\n", name));
+
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &tmp);
+ if (tmp)
+ {
+ strcpy (dest, tmp);
+ free (tmp);
+ return SANE_TRUE;
+ }
+ }
+ }
+ return SANE_FALSE;
+}
+
+#ifdef ARTEC48U_USE_BUTTONS
+static SANE_Status
+artec48u_check_buttons (Artec48U_Device * dev, SANE_Int * value)
+{
+ SANE_Status status;
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x74;
+ req[1] = 0x01;
+
+ status = artec48u_device_small_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ *value = (SANE_Int) req[2];
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+#define MAX_DOWNLOAD_BLOCK_SIZE 64
+static SANE_Status
+artec48u_generic_start_scan (Artec48U_Device * dev)
+{
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x43;
+ req[1] = 0x01;
+
+ return artec48u_device_req (dev, req, req);
+
+}
+
+static SANE_Status
+artec48u_generic_read_scanned_data (Artec48U_Device * dev, SANE_Bool * ready)
+{
+ SANE_Status status;
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x35;
+ req[1] = 0x01;
+
+ status = artec48u_device_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (req[1] == 0x35)
+ {
+ if (req[0] == 0)
+ *ready = SANE_TRUE;
+ else
+ *ready = SANE_FALSE;
+ }
+ else
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_download_firmware (Artec48U_Device * dev,
+ SANE_Byte * data, SANE_Word size)
+{
+ SANE_Status status;
+ SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE];
+ SANE_Byte *block;
+ SANE_Word addr, bytes_left;
+ Artec48U_Packet boot_req;
+ SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE;
+
+ CHECK_DEV_ACTIVE ((Artec48U_Device *) dev,
+ (char *) "artec48u_device_download_firmware");
+
+ for (addr = 0; addr < size; addr += block_size)
+ {
+ bytes_left = size - addr;
+ if (bytes_left > block_size)
+ block = data + addr;
+ else
+ {
+ memset (download_buf, 0, block_size);
+ memcpy (download_buf, data + addr, bytes_left);
+ block = download_buf;
+ }
+ status = artec48u_device_memory_write (dev, addr, block_size, block);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ status = artec48u_device_memory_read (dev, addr, block_size, check_buf);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ if (memcmp (block, check_buf, block_size) != 0)
+ {
+ XDBG ((3,
+ "artec48u_device_download_firmware: mismatch at block 0x%0x\n",
+ addr));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ memset (boot_req, 0, sizeof (boot_req));
+ boot_req[0] = 0x69;
+ boot_req[1] = 0x01;
+ boot_req[2] = LOBYTE (addr);
+ boot_req[3] = HIBYTE (addr);
+ status = artec48u_device_req (dev, boot_req, boot_req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_is_moving (Artec48U_Device * dev, SANE_Bool * moving)
+{
+ SANE_Status status;
+ Artec48U_Packet req;
+ memset (req, 0, sizeof (req));
+ req[0] = 0x17;
+ req[1] = 0x01;
+
+ status = artec48u_device_req (dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ if (req[0] == 0x00 && req[1] == 0x17)
+ {
+ if (req[2] == 0 && (req[3] == 0 || req[3] == 2))
+ *moving = SANE_FALSE;
+ else
+ *moving = SANE_TRUE;
+ }
+ else
+ return SANE_STATUS_IO_ERROR;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_carriage_home (Artec48U_Device * dev)
+{
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x24;
+ req[1] = 0x01;
+
+ return artec48u_device_req (dev, req, req);
+}
+
+
+static SANE_Status
+artec48u_stop_scan (Artec48U_Device * dev)
+{
+ Artec48U_Packet req;
+
+ memset (req, 0, sizeof (req));
+ req[0] = 0x41;
+ req[1] = 0x01;
+ return artec48u_device_small_req (dev, req, req);
+}
+
+
+static SANE_Status
+artec48u_setup_scan (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Action action,
+ SANE_Bool calculate_only,
+ Artec48U_Scan_Parameters * params)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_setup_scan") SANE_Status status;
+ SANE_Int xdpi, ydpi;
+ SANE_Bool color;
+ SANE_Int depth;
+ SANE_Int pixel_x0, pixel_y0, pixel_xs, pixel_ys;
+ SANE_Int pixel_align;
+
+ SANE_Int abs_x0, abs_y0, abs_xs, abs_ys, base_xdpi, base_ydpi;
+ SANE_Int scan_xs, scan_ys, scan_bpl;
+ SANE_Int bits_per_line;
+ SANE_Byte color_mode_code;
+
+ /*If we scan a black line, we use these exposure values */
+ Artec48U_Exposure_Parameters exp_params_black = { 4, 4, 4 };
+
+ XDBG ((6, "%s: enter\n", function_name));
+ XDBG ((1,"setup scan is_epro %d\n",s->dev->is_epro));
+ XDBG ((1,"setup scan epro_mult %d\n",s->dev->epro_mult));
+
+ xdpi = request->xdpi;
+ ydpi = request->ydpi;
+ color = request->color;
+ depth = request->depth;
+
+ switch (action)
+ {
+ case SA_CALIBRATE_SCAN_WHITE:
+ {
+ /*move a bit inside scan mark -
+ the value for the offset was found by trial and error */
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_w;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE_SCAN_OFFSET_1:
+ case SA_CALIBRATE_SCAN_OFFSET_2:
+ {
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_b;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE_SCAN_EXPOSURE_1:
+ case SA_CALIBRATE_SCAN_EXPOSURE_2:
+ {
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_w;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_CALIBRATE_SCAN_BLACK:
+ {
+ pixel_y0 = s->dev->shading_offset;
+ pixel_ys = s->dev->shading_lines_w;
+ pixel_x0 = 0;
+ pixel_xs = 5120 * s->dev->epro_mult; /*epro*/
+ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/
+ color = SANE_TRUE;
+ depth = 8;
+ break;
+ }
+ case SA_SCAN:
+ {
+ SANE_Fixed x0 = request->x0 + s->dev->xdpi_offset;
+ SANE_Fixed y0;
+ /*epro*/
+ if ((ydpi == 1200) && (s->dev->is_epro == 0))
+ xdpi = 600;
+ y0 = request->y0 + s->dev->ydpi_offset;
+ pixel_ys = SANE_UNFIX (request->ys) * ydpi / MM_PER_INCH + 0.5;
+ pixel_x0 = SANE_UNFIX (x0) * xdpi / MM_PER_INCH + 0.5;
+ pixel_y0 = SANE_UNFIX (y0) * ydpi / MM_PER_INCH + 0.5;
+ pixel_xs = SANE_UNFIX (request->xs) * xdpi / MM_PER_INCH + 0.5;
+ break;
+ }
+
+ default:
+ XDBG ((6, "%s: invalid action=%d\n", function_name, (int) action));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((6, "%s: xdpi=%d, ydpi=%d\n", function_name, xdpi, ydpi));
+ XDBG ((6, "%s: color=%s, depth=%d\n", function_name,
+ color ? "TRUE" : "FALSE", depth));
+ XDBG ((6, "%s: pixel_x0=%d, pixel_y0=%d\n", function_name,
+ pixel_x0, pixel_y0));
+ XDBG ((6, "%s: pixel_xs=%d, pixel_ys=%d\n", function_name,
+ pixel_xs, pixel_ys));
+
+ switch (depth)
+ {
+ case 8:
+ color_mode_code = color ? 0x84 : 0x82;
+ break;
+
+ case 16:
+ color_mode_code = color ? 0xa4 : 0xa2;
+ break;
+
+ default:
+ XDBG ((6, "%s: unsupported depth=%d\n", function_name, depth));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ base_xdpi = s->dev->optical_xdpi;
+ base_ydpi = s->dev->base_ydpi;
+
+ XDBG ((6, "%s: base_xdpi=%d, base_ydpi=%d\n", function_name,
+ base_xdpi, base_ydpi));
+
+ abs_x0 = pixel_x0 * base_xdpi / xdpi;
+ abs_y0 = pixel_y0 * base_ydpi / ydpi;
+
+ /* Calculate minimum number of pixels which span an integral multiple of 64
+ * bytes. */
+ pixel_align = 32; /* best case for depth = 16 */
+ while ((depth * pixel_align) % (64 * 8) != 0)
+ pixel_align *= 2;
+ XDBG ((6, "%s: pixel_align=%d\n", function_name, pixel_align));
+
+ if (pixel_xs % pixel_align == 0)
+ scan_xs = pixel_xs;
+ else
+ scan_xs = (pixel_xs / pixel_align + 1) * pixel_align;
+ scan_ys = pixel_ys;
+ XDBG ((6, "%s: scan_xs=%d, scan_ys=%d\n", function_name, scan_xs, scan_ys));
+
+ abs_xs = scan_xs * base_xdpi / xdpi;
+ abs_ys = scan_ys * base_ydpi / ydpi;
+ XDBG ((6, "%s: abs_xs=%d, abs_ys=%d\n", function_name, abs_xs, abs_ys));
+
+ bits_per_line = depth * scan_xs;
+ if (bits_per_line % 8) /* impossible */
+ {
+ XDBG ((1, "%s: BUG: unaligned bits_per_line=%d\n", function_name,
+ bits_per_line));
+ return SANE_STATUS_INVAL;
+ }
+ scan_bpl = bits_per_line / 8;
+
+ if (scan_bpl % 64) /* impossible */
+ {
+ XDBG ((1, "%s: BUG: unaligned scan_bpl=%d\n", function_name, scan_bpl));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (scan_bpl > 15600)
+ {
+ XDBG ((6, "%s: scan_bpl=%d, too large\n", function_name, scan_bpl));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((6, "%s: scan_bpl=%d\n", function_name, scan_bpl));
+
+ if (!calculate_only)
+ {
+ Artec48U_Packet req;
+ char motor_mode_1, motor_mode_2;
+ switch (action)
+ {
+ case SA_CALIBRATE_SCAN_WHITE:
+ motor_mode_1 = 0x01;
+ motor_mode_2 = 0x00;
+ break;
+
+ case SA_CALIBRATE_SCAN_BLACK:
+ motor_mode_1 = 0x04;
+ motor_mode_2 = 0x00;
+ break;
+
+ case SA_SCAN:
+ motor_mode_1 = 0x01;
+ motor_mode_2 = 0x00;
+ break;
+
+ default:
+ XDBG ((6, "%s: invalid action=%d\n", function_name, (int) action));
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Fill in the setup command */
+ memset (req, 0, sizeof (req));
+ req[0x00] = 0x20;
+ req[0x01] = 0x01;
+ req[0x02] = LOBYTE (abs_y0);
+ req[0x03] = HIBYTE (abs_y0);
+ req[0x04] = LOBYTE (abs_ys);
+ req[0x05] = HIBYTE (abs_ys);
+ req[0x06] = LOBYTE (abs_x0);
+ req[0x07] = HIBYTE (abs_x0);
+ req[0x08] = LOBYTE (abs_xs);
+ req[0x09] = HIBYTE (abs_xs);
+ req[0x0a] = color_mode_code;
+ req[0x0b] = 0x60;
+ req[0x0c] = LOBYTE (xdpi);
+ req[0x0d] = HIBYTE (xdpi);
+ req[0x0e] = 0x12;
+ req[0x0f] = 0x00;
+ req[0x10] = LOBYTE (scan_bpl);
+ req[0x11] = HIBYTE (scan_bpl);
+ req[0x12] = LOBYTE (scan_ys);
+ req[0x13] = HIBYTE (scan_ys);
+ req[0x14] = motor_mode_1;
+ req[0x15] = motor_mode_2;
+ req[0x16] = LOBYTE (ydpi);
+ req[0x17] = HIBYTE (ydpi);
+ req[0x18] = 0x00;
+
+ status = artec48u_device_req (s->dev, req, req);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: setup request failed: %s\n", function_name,
+ sane_strstatus (status)));
+ return status;
+ }
+
+ if (action == SA_SCAN)
+ {
+ artec48u_calculate_shading_buffer (s, pixel_x0, pixel_xs + pixel_x0,
+ xdpi, color);
+ artec48u_generic_set_exposure_time (s->dev,
+ &(s->dev->
+ artec_48u_exposure_params));
+ artec48u_generic_set_afe (s->dev, &(s->dev->artec_48u_afe_params));
+ }
+ else if (action == SA_CALIBRATE_SCAN_BLACK)
+ {
+ artec48u_generic_set_exposure_time (s->dev, &exp_params_black);
+ artec48u_generic_set_afe (s->dev, &(s->dev->afe_params));
+ }
+ else if (action == SA_CALIBRATE_SCAN_WHITE)
+ {
+ artec48u_generic_set_exposure_time (s->dev, &(s->dev->exp_params));
+ artec48u_generic_set_afe (s->dev, &(s->dev->afe_params));
+ }
+ }
+ /* Fill in calculated values */
+ params->xdpi = xdpi;
+ params->ydpi = ydpi;
+ params->depth = depth;
+ params->color = color;
+ params->pixel_xs = pixel_xs;
+ params->pixel_ys = pixel_ys;
+ params->scan_xs = scan_xs;
+ params->scan_ys = scan_ys;
+ params->scan_bpl = scan_bpl;
+
+ XDBG ((6, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_generic_set_afe (Artec48U_Device * dev,
+ Artec48U_AFE_Parameters * params)
+{
+ Artec48U_Packet req;
+ memset (req, 0, sizeof (req));
+ req[0] = 0x22;
+ req[1] = 0x01;
+ req[2] = params->r_offset;
+ req[3] = params->r_pga;
+ req[4] = params->g_offset;
+ req[5] = params->g_pga;
+ req[6] = params->b_offset;
+ req[7] = params->b_pga;
+
+ return artec48u_device_req (dev, req, req);
+}
+
+
+static SANE_Status
+artec48u_generic_set_exposure_time (Artec48U_Device * dev,
+ Artec48U_Exposure_Parameters * params)
+{
+ Artec48U_Packet req;
+ memset (req, 0, sizeof (req));
+ req[0] = 0x76;
+ req[1] = 0x01;
+ req[2] = req[6] = req[10] = 0x04;
+ req[4] = LOBYTE (params->r_time);
+ req[5] = HIBYTE (params->r_time);
+ req[8] = LOBYTE (params->g_time);
+ req[9] = HIBYTE (params->g_time);
+ req[12] = LOBYTE (params->b_time);
+ req[13] = HIBYTE (params->b_time);
+ return artec48u_device_req (dev, req, req);
+}
+
+static SANE_Status
+artec48u_device_new (Artec48U_Device ** dev_return)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_new") Artec48U_Device *dev;
+
+ XDBG ((7, "%s: enter\n", function_name));
+ if (!dev_return)
+ return SANE_STATUS_INVAL;
+
+ dev = (Artec48U_Device *) malloc (sizeof (Artec48U_Device));
+
+ if (!dev)
+ {
+ XDBG ((3, "%s: couldn't malloc %lu bytes for device\n",
+ function_name, (u_long) sizeof (Artec48U_Device)));
+ *dev_return = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ *dev_return = dev;
+
+ memset (dev, 0, sizeof (Artec48U_Device));
+
+ dev->fd = -1;
+ dev->active = SANE_FALSE;
+
+ dev->read_buffer = NULL;
+ dev->requested_buffer_size = 32768;
+
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_free (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_free")
+ XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev));
+ if (dev)
+ {
+ if (dev->active)
+ artec48u_device_deactivate (dev);
+
+ if (dev->fd != -1)
+ artec48u_device_close (dev);
+
+ XDBG ((7, "%s: freeing dev\n", function_name));
+ free (dev);
+ }
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_open (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_open")
+ SANE_Status status;
+ SANE_Int fd;
+
+ XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev));
+
+ CHECK_DEV_NOT_NULL (dev, function_name);
+
+ if (dev->fd != -1)
+ {
+ XDBG ((3, "%s: device already open\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_usb_open (dev->sane.name, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: sanei_usb_open failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ dev->fd = fd;
+
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_close (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_close")
+ XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev));
+
+ CHECK_DEV_OPEN (dev, function_name);
+
+ if (dev->active)
+ artec48u_device_deactivate (dev);
+
+ sanei_usb_close (dev->fd);
+ dev->fd = -1;
+
+ XDBG ((7, "%s: leave: ok\n", function_name));
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_activate (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_activate")
+ CHECK_DEV_OPEN (dev, function_name);
+
+ if (dev->active)
+ {
+ XDBG ((3, "%s: device already active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((7, "%s: model \"%s\"\n", function_name, dev->sane.model));
+
+ dev->xdpi_offset = SANE_FIX (dev->xdpi_offset *
+ MM_PER_INCH / dev->optical_xdpi);
+ dev->ydpi_offset = SANE_FIX (dev->ydpi_offset *
+ MM_PER_INCH / dev->optical_ydpi);
+
+ dev->active = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_deactivate (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_deactivate")
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (dev->read_active)
+ artec48u_device_read_finish (dev);
+
+ dev->active = SANE_FALSE;
+
+ return status;
+}
+
+static SANE_Status
+artec48u_device_memory_write (Artec48U_Device * dev,
+ SANE_Word addr,
+ SANE_Word size, SANE_Byte * data)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_memory_write")
+ SANE_Status status;
+
+ XDBG ((8, "%s: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ function_name, (void *) dev, addr, size, (void *) data));
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ status = sanei_usb_control_msg (dev->fd, 0x40, 0x01,
+ memory_write_value, addr, size, data);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: sanei_usb_control_msg failed: %s\n",
+ function_name, sane_strstatus (status)));
+ }
+
+ return status;
+}
+
+static SANE_Status
+artec48u_device_memory_read (Artec48U_Device * dev,
+ SANE_Word addr, SANE_Word size, SANE_Byte * data)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_memory_read")
+ SANE_Status status;
+
+ XDBG ((8, "%s: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ function_name, (void *) dev, addr, size, data));
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ status = sanei_usb_control_msg (dev->fd, 0xc0, 0x01,
+ memory_read_value, addr, size, data);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: sanei_usb_control_msg failed: %s\n",
+ function_name, sane_strstatus (status)));
+ }
+
+ return status;
+}
+
+static SANE_Status
+artec48u_device_generic_req (Artec48U_Device * dev,
+ SANE_Word cmd_value, SANE_Word cmd_index,
+ SANE_Word res_value, SANE_Word res_index,
+ Artec48U_Packet cmd, Artec48U_Packet res)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_generic_req")
+ SANE_Status status;
+
+ XDBG ((7, "%s: command=0x%02x\n", function_name, cmd[0]));
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ status = sanei_usb_control_msg (dev->fd,
+ 0x40, 0x01, cmd_value, cmd_index,
+ ARTEC48U_PACKET_SIZE, cmd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: writing command failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ memset (res, 0, sizeof (Artec48U_Packet));
+
+ status = sanei_usb_control_msg (dev->fd,
+ 0xc0, 0x01, res_value, res_index,
+ ARTEC48U_PACKET_SIZE, res);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: reading response failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+ return status;
+}
+
+static SANE_Status
+artec48u_device_req (Artec48U_Device * dev, Artec48U_Packet cmd,
+ Artec48U_Packet res)
+{
+ return artec48u_device_generic_req (dev,
+ send_cmd_value,
+ send_cmd_index,
+ recv_res_value,
+ recv_res_index, cmd, res);
+}
+
+static SANE_Status
+artec48u_device_small_req (Artec48U_Device * dev, Artec48U_Packet cmd,
+ Artec48U_Packet res)
+{
+ Artec48U_Packet fixed_cmd;
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ memcpy (fixed_cmd + i * 8, cmd, 8);
+
+ return artec48u_device_generic_req (dev,
+ send_small_cmd_value,
+ send_small_cmd_index,
+ recv_small_res_value,
+ recv_small_res_index, fixed_cmd, res);
+}
+
+static SANE_Status
+artec48u_device_read_raw (Artec48U_Device * dev, SANE_Byte * buffer,
+ size_t * size)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read_raw")
+ SANE_Status status;
+
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ XDBG ((7, "%s: enter: size=0x%lx\n", function_name, (unsigned long) *size));
+
+ status = sanei_usb_read_bulk (dev->fd, buffer, size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: bulk read failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ XDBG ((7, "%s: leave: size=0x%lx\n", function_name, (unsigned long) *size));
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_set_read_buffer_size (Artec48U_Device * dev,
+ size_t buffer_size)
+{
+ DECLARE_FUNCTION_NAME ("gt68xx_device_set_read_buffer_size")
+ CHECK_DEV_NOT_NULL (dev, function_name);
+
+ if (dev->read_active)
+ {
+ XDBG ((3, "%s: BUG: read already active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ buffer_size = (buffer_size + 63UL) & ~63UL;
+ if (buffer_size > 0)
+ {
+ dev->requested_buffer_size = buffer_size;
+ return SANE_STATUS_GOOD;
+ }
+
+ XDBG ((3, "%s: bad buffer size\n", function_name));
+ return SANE_STATUS_INVAL;
+}
+
+static SANE_Status
+artec48u_device_read_prepare (Artec48U_Device * dev, size_t expected_count)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read_prepare")
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (dev->read_active)
+ {
+ XDBG ((3, "%s: read already active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ dev->read_buffer = (SANE_Byte *) malloc (dev->requested_buffer_size);
+ if (!dev->read_buffer)
+ {
+ XDBG ((3, "%s: not enough memory for the read buffer (%lu bytes)\n",
+ function_name, (unsigned long) dev->requested_buffer_size));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->read_active = SANE_TRUE;
+ dev->read_pos = dev->read_bytes_in_buffer = 0;
+ dev->read_bytes_left = expected_count;
+
+ return SANE_STATUS_GOOD;
+}
+
+static RETSIGTYPE
+reader_process_sigterm_handler (int signal)
+{
+ XDBG ((1, "reader_process: terminated by signal %d\n", signal));
+ _exit (SANE_STATUS_GOOD);
+}
+
+static RETSIGTYPE
+usb_reader_process_sigterm_handler (int signal)
+{
+ XDBG ((1, "reader_process (usb): terminated by signal %d\n", signal));
+ cancelRead = SANE_TRUE;
+}
+
+static SANE_Status
+artec48u_device_read_start (Artec48U_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "artec48u_device_read_start");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_read (Artec48U_Device * dev, SANE_Byte * buffer,
+ size_t * size)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read") SANE_Status status;
+ size_t byte_count = 0;
+ size_t left_to_read = *size;
+ size_t transfer_size, block_size, raw_block_size;
+
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (!dev->read_active)
+ {
+ XDBG ((3, "%s: read not active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ while (left_to_read > 0)
+ {
+ if (dev->read_bytes_in_buffer == 0)
+ {
+ block_size = dev->requested_buffer_size;
+ if (block_size > dev->read_bytes_left)
+ block_size = dev->read_bytes_left;
+ if (block_size == 0)
+ break;
+ raw_block_size = (block_size + 63UL) & ~63UL;
+ status = artec48u_device_read_raw (dev, dev->read_buffer,
+ &raw_block_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: read failed\n", function_name));
+ return status;
+ }
+ dev->read_pos = 0;
+ dev->read_bytes_in_buffer = block_size;
+ dev->read_bytes_left -= block_size;
+ }
+
+ transfer_size = left_to_read;
+ if (transfer_size > dev->read_bytes_in_buffer)
+ transfer_size = dev->read_bytes_in_buffer;
+ if (transfer_size > 0)
+ {
+ memcpy (buffer, dev->read_buffer + dev->read_pos, transfer_size);
+ dev->read_pos += transfer_size;
+ dev->read_bytes_in_buffer -= transfer_size;
+ byte_count += transfer_size;
+ left_to_read -= transfer_size;
+ buffer += transfer_size;
+ }
+ }
+
+ *size = byte_count;
+
+ if (byte_count == 0)
+ return SANE_STATUS_EOF;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_device_read_finish (Artec48U_Device * dev)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_device_read_finish")
+ CHECK_DEV_ACTIVE (dev, function_name);
+
+ if (!dev->read_active)
+ {
+ XDBG ((3, "%s: read not active\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ XDBG ((7, "%s: read_bytes_left = %ld\n",
+ function_name, (long) dev->read_bytes_left));
+
+ free (dev->read_buffer);
+ dev->read_buffer = NULL;
+
+ dev->read_active = SANE_FALSE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_delay_buffer_init (Artec48U_Delay_Buffer * delay,
+ SANE_Int pixels_per_line)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_delay_buffer_init")
+ SANE_Int bytes_per_line;
+ SANE_Int line_count, i;
+
+ if (pixels_per_line <= 0)
+ {
+ XDBG ((3, "%s: BUG: pixels_per_line=%d\n",
+ function_name, pixels_per_line));
+ return SANE_STATUS_INVAL;
+ }
+
+ bytes_per_line = pixels_per_line * sizeof (unsigned int);
+
+ delay->line_count = line_count = 1;
+ delay->read_index = 0;
+ delay->write_index = 0;
+
+ delay->mem_block = (SANE_Byte *) malloc (bytes_per_line * line_count);
+ if (!delay->mem_block)
+ {
+ XDBG ((3, "%s: no memory for delay block\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ delay->lines =
+ (unsigned int **) malloc (sizeof (unsigned int *) * line_count);
+ if (!delay->lines)
+ {
+ free (delay->mem_block);
+ XDBG ((3, "%s: no memory for delay line pointers\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for (i = 0; i < line_count; ++i)
+ delay->lines[i] =
+ (unsigned int *) (delay->mem_block + i * bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_delay_buffer_done (Artec48U_Delay_Buffer * delay)
+{
+ if (delay->lines)
+ {
+ free (delay->lines);
+ delay->lines = NULL;
+ }
+
+ if (delay->mem_block)
+ {
+ free (delay->mem_block);
+ delay->mem_block = NULL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+#define DELAY_BUFFER_WRITE_PTR(delay) ( (delay)->lines[(delay)->write_index] )
+
+#define DELAY_BUFFER_READ_PTR(delay) ( (delay)->lines[(delay)->read_index ] )
+
+#define DELAY_BUFFER_STEP(delay) \
+ do { \
+ (delay)->read_index = ((delay)->read_index + 1) % (delay)->line_count; \
+ (delay)->write_index = ((delay)->write_index + 1) % (delay)->line_count; \
+ } while (SANE_FALSE)
+
+
+static inline void
+unpack_8_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line)
+{
+ XDBG ((3, "unpack_8_mono\n"));
+ for (; pixels_per_line > 0; ++src, ++dst, --pixels_per_line)
+ {
+ *dst = (((unsigned int) *src) << 8) | *src;
+ }
+}
+
+static inline void
+unpack_16_le_mono (SANE_Byte * src, unsigned int *dst,
+ SANE_Int pixels_per_line)
+{
+ XDBG ((3, "unpack_16_le_mono\n"));
+ for (; pixels_per_line > 0; src += 2, dst++, --pixels_per_line)
+ {
+ *dst = (((unsigned int) src[1]) << 8) | src[0];
+ }
+}
+
+static SANE_Status
+line_read_gray_8 (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+ XDBG ((3, "line_read_gray_8\n"));
+
+ size = reader->params.scan_bpl;
+ status = artec48u_device_read (reader->dev, reader->pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_8_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_gray_16 (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ unsigned int *buffer;
+
+ XDBG ((3, "line_read_gray_16\n"));
+ size = reader->params.scan_bpl;
+ status = artec48u_device_read (reader->dev, reader->pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[0] = buffer;
+ unpack_16_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_8_line_mode (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+ XDBG ((3, "line_read_bgr_8_line_mode\n"));
+
+ size = reader->params.scan_bpl * 3;
+ status = artec48u_device_read (reader->dev, pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_8_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+line_read_bgr_16_line_mode (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ SANE_Status status;
+ size_t size;
+ SANE_Int pixels_per_line;
+ SANE_Byte *pixel_buffer = reader->pixel_buffer;
+
+ XDBG ((3, "line_read_bgr_16_line_mode\n"));
+ size = reader->params.scan_bpl * 3;
+ status = artec48u_device_read (reader->dev, pixel_buffer, &size);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ pixels_per_line = reader->pixels_per_line;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->b_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->g_delay),
+ pixels_per_line);
+ pixel_buffer += reader->params.scan_bpl;
+ unpack_16_le_mono (pixel_buffer,
+ DELAY_BUFFER_WRITE_PTR (&reader->r_delay),
+ pixels_per_line);
+
+ buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay);
+ buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay);
+ buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay);
+
+ DELAY_BUFFER_STEP (&reader->r_delay);
+ DELAY_BUFFER_STEP (&reader->g_delay);
+ DELAY_BUFFER_STEP (&reader->b_delay);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_line_reader_init_delays (Artec48U_Line_Reader * reader)
+{
+ SANE_Status status;
+
+ if (reader->params.color)
+ {
+ status = artec48u_delay_buffer_init (&reader->r_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ status = artec48u_delay_buffer_init (&reader->g_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ artec48u_delay_buffer_done (&reader->r_delay);
+ return status;
+ }
+
+ status = artec48u_delay_buffer_init (&reader->b_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ {
+ artec48u_delay_buffer_done (&reader->g_delay);
+ artec48u_delay_buffer_done (&reader->r_delay);
+ return status;
+ }
+ }
+ else
+ {
+ status = artec48u_delay_buffer_init (&reader->g_delay,
+ reader->params.pixel_xs);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ }
+
+ reader->delays_initialized = SANE_TRUE;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+artec48u_line_reader_free_delays (Artec48U_Line_Reader * reader)
+{
+ if (!reader)
+ {
+ return;
+ }
+ if (reader->delays_initialized)
+ {
+ if (reader->params.color)
+ {
+ artec48u_delay_buffer_done (&reader->b_delay);
+ artec48u_delay_buffer_done (&reader->g_delay);
+ artec48u_delay_buffer_done (&reader->r_delay);
+ }
+ else
+ {
+ artec48u_delay_buffer_done (&reader->g_delay);
+ }
+ reader->delays_initialized = SANE_FALSE;
+ }
+}
+
+static SANE_Status
+artec48u_line_reader_new (Artec48U_Device * dev,
+ Artec48U_Scan_Parameters * params,
+ Artec48U_Line_Reader ** reader_return)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_line_reader_new") SANE_Status status;
+ Artec48U_Line_Reader *reader;
+ SANE_Int image_size;
+ SANE_Int scan_bpl_full;
+
+ XDBG ((6, "%s: enter\n", function_name));
+ XDBG ((6, "%s: enter params xdpi: %i\n", function_name, params->xdpi));
+ XDBG ((6, "%s: enter params ydpi: %i\n", function_name, params->ydpi));
+ XDBG ((6, "%s: enter params depth: %i\n", function_name, params->depth));
+ XDBG ((6, "%s: enter params color: %i\n", function_name, params->color));
+ XDBG ((6, "%s: enter params pixel_xs: %i\n", function_name, params->pixel_xs));
+ XDBG ((6, "%s: enter params pixel_ys: %i\n", function_name, params->pixel_ys));
+ XDBG ((6, "%s: enter params scan_xs: %i\n", function_name, params->scan_xs));
+ XDBG ((6, "%s: enter params scan_ys: %i\n", function_name, params->scan_ys));
+ XDBG ((6, "%s: enter params scan_bpl: %i\n", function_name, params->scan_bpl));
+ *reader_return = NULL;
+
+ reader = (Artec48U_Line_Reader *) malloc (sizeof (Artec48U_Line_Reader));
+ if (!reader)
+ {
+ XDBG ((3, "%s: cannot allocate Artec48U_Line_Reader\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+ memset (reader, 0, sizeof (Artec48U_Line_Reader));
+
+ reader->dev = dev;
+ memcpy (&reader->params, params, sizeof (Artec48U_Scan_Parameters));
+ reader->pixel_buffer = 0;
+ reader->delays_initialized = SANE_FALSE;
+
+ reader->read = NULL;
+
+ status = artec48u_line_reader_init_delays (reader);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: cannot allocate line buffers: %s\n",
+ function_name, sane_strstatus (status)));
+ free (reader);
+ return status;
+ }
+
+ reader->pixels_per_line = reader->params.pixel_xs;
+
+ if (!reader->params.color)
+ {
+ XDBG ((2, "!reader->params.color\n"));
+ if (reader->params.depth == 8)
+ reader->read = line_read_gray_8;
+ else if (reader->params.depth == 16)
+ reader->read = line_read_gray_16;
+ }
+ else
+ {
+ XDBG ((2, "reader line mode\n"));
+ if (reader->params.depth == 8)
+ {
+ XDBG ((2, "depth 8\n"));
+ reader->read = line_read_bgr_8_line_mode;
+ }
+ else if (reader->params.depth == 16)
+ {
+ XDBG ((2, "depth 16\n"));
+ reader->read = line_read_bgr_16_line_mode;
+ }
+ }
+
+ if (reader->read == NULL)
+ {
+ XDBG ((3, "%s: unsupported bit depth (%d)\n",
+ function_name, reader->params.depth));
+ artec48u_line_reader_free_delays (reader);
+ free (reader);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ scan_bpl_full = reader->params.scan_bpl;
+ if (reader->params.color)
+ scan_bpl_full *= 3;
+
+ reader->pixel_buffer = malloc (scan_bpl_full);
+ if (!reader->pixel_buffer)
+ {
+ XDBG ((3, "%s: cannot allocate pixel buffer\n", function_name));
+ artec48u_line_reader_free_delays (reader);
+ free (reader);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ artec48u_device_set_read_buffer_size (reader->dev,
+ scan_bpl_full /* 200 */ );
+
+ image_size = scan_bpl_full * reader->params.scan_ys;
+ status = artec48u_device_read_prepare (reader->dev, image_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: artec48u_device_read_prepare failed: %s\n",
+ function_name, sane_strstatus (status)));
+ free (reader->pixel_buffer);
+ artec48u_line_reader_free_delays (reader);
+ free (reader);
+ return status;
+ }
+
+ XDBG ((6, "%s: leave: ok\n", function_name));
+ *reader_return = reader;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_line_reader_free (Artec48U_Line_Reader * reader)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_line_reader_free") SANE_Status status;
+
+ XDBG ((6, "%s: enter\n", function_name));
+
+ if (!reader)
+ {
+ return SANE_STATUS_GOOD;
+ }
+ artec48u_line_reader_free_delays (reader);
+
+ if (reader->pixel_buffer)
+ {
+ free (reader->pixel_buffer);
+ reader->pixel_buffer = NULL;
+ }
+
+ status = artec48u_device_read_finish (reader->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "%s: artec48u_device_read_finish failed: %s\n",
+ function_name, sane_strstatus (status)));
+ }
+
+ if (reader)
+ free (reader);
+
+ XDBG ((6, "%s: leave\n", function_name));
+ return status;
+}
+
+static SANE_Status
+artec48u_line_reader_read (Artec48U_Line_Reader * reader,
+ unsigned int **buffer_pointers_return)
+{
+ return (*reader->read) (reader, buffer_pointers_return);
+}
+
+static SANE_Status
+artec48u_scanner_new (Artec48U_Device * dev,
+ Artec48U_Scanner ** scanner_return)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_new") Artec48U_Scanner *s;
+
+ *scanner_return = NULL;
+
+ s = (Artec48U_Scanner *) malloc (sizeof (Artec48U_Scanner));
+ if (!s)
+ {
+ XDBG ((5, "%s: no memory for Artec48U_Scanner\n", function_name));
+ return SANE_STATUS_NO_MEM;
+ }
+ s->dev = dev;
+ s->reader = NULL;
+ s->scanning = SANE_FALSE;
+ s->line_buffer = NULL;
+ s->lineart_buffer = NULL;
+ s->next = NULL;
+ s->pipe_handle = NULL;
+ s->buffer_pointers[0] = NULL;
+ s->buffer_pointers[1] = NULL;
+ s->buffer_pointers[2] = NULL;
+ s->shading_buffer_w = NULL;
+ s->shading_buffer_b = NULL;
+ s->shading_buffer_white[0] = NULL;
+ s->shading_buffer_white[1] = NULL;
+ s->shading_buffer_white[2] = NULL;
+ s->shading_buffer_black[0] = NULL;
+ s->shading_buffer_black[1] = NULL;
+ s->shading_buffer_black[2] = NULL;
+ *scanner_return = s;
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_read_line (Artec48U_Scanner * s,
+ unsigned int **buffer_pointers, SANE_Bool shading)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_read_line") SANE_Status status;
+ int i, j, c;
+
+ status = artec48u_line_reader_read (s->reader, buffer_pointers);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((5, "%s: artec48u_line_reader_read failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+ if (shading != SANE_TRUE)
+ return status;
+
+ c = s->reader->pixels_per_line;
+ if (s->reader->params.color == SANE_TRUE)
+ {
+ for (i = c - 1; i >= 0; i--)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ int new_value;
+ unsigned int value = buffer_pointers[j][i];
+ if (value < s->shading_buffer_black[j][i])
+ value = s->shading_buffer_black[j][i];
+ if (value > s->shading_buffer_white[j][i])
+ value = s->shading_buffer_white[j][i];
+ new_value =
+ (double) (value -
+ s->shading_buffer_black[j][i]) * 65535.0 /
+ (double) (s->shading_buffer_white[j][i] -
+ s->shading_buffer_black[j][i]);
+ if (new_value < 0)
+ new_value = 0;
+ if (new_value > 65535)
+ new_value = 65535;
+ new_value =
+ s->gamma_array[j +
+ 1][s->contrast_array[s->
+ brightness_array
+ [new_value]]];
+ new_value = s->gamma_array[0][new_value];
+ buffer_pointers[j][i] = new_value;
+ }
+ }
+ }
+ else
+ {
+ for (i = c - 1; i >= 0; i--)
+ {
+ int new_value;
+ unsigned int value = buffer_pointers[0][i];
+ new_value =
+ (double) (value -
+ s->shading_buffer_black[1][i]) * 65535.0 /
+ (double) (s->shading_buffer_white[1][i] -
+ s->shading_buffer_black[1][i]);
+ if (new_value < 0)
+ new_value = 0;
+ if (new_value > 65535)
+ new_value = 65535;
+ new_value =
+ s->gamma_array[0][s->
+ contrast_array[s->brightness_array[new_value]]];
+ buffer_pointers[0][i] = new_value;
+ }
+ }
+ return status;
+}
+
+static SANE_Status
+artec48u_scanner_free (Artec48U_Scanner * s)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_free") if (!s)
+ {
+ XDBG ((5, "%s: scanner==NULL\n", function_name));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (s->reader)
+ {
+ artec48u_line_reader_free (s->reader);
+ s->reader = NULL;
+ }
+
+ free (s->shading_buffer_w);
+ free (s->shading_buffer_b);
+ free (s->shading_buffer_white[0]);
+ free (s->shading_buffer_black[0]);
+ free (s->shading_buffer_white[1]);
+ free (s->shading_buffer_black[1]);
+ free (s->shading_buffer_white[2]);
+ free (s->shading_buffer_black[2]);
+
+ if (s->line_buffer)
+ free (s->line_buffer);
+ if (s->lineart_buffer)
+ free (s->lineart_buffer);
+
+ free (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_internal_start_scan (Artec48U_Scanner * s)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_internal_start_scan")
+ SANE_Status status;
+ SANE_Bool ready;
+ SANE_Int repeat_count;
+
+ status = artec48u_wait_for_positioning (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_scanner_wait_for_positioning error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ status = artec48u_generic_start_scan (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_start_scan error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ for (repeat_count = 0; repeat_count < 30 * 10; ++repeat_count)
+ {
+ status = artec48u_generic_read_scanned_data (s->dev, &ready);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_read_scanned_data error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+ if (ready)
+ break;
+ usleep (100000);
+ }
+
+ if (!ready)
+ {
+ XDBG ((2, "%s: scanner still not ready - giving up\n", function_name));
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ status = artec48u_device_read_start (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_read_start error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_start_scan_extended (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Action action,
+ Artec48U_Scan_Parameters * params)
+{
+ DECLARE_FUNCTION_NAME ("artec48u_scanner_start_scan_extended")
+ SANE_Status status;
+
+ status = artec48u_wait_for_positioning (s->dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_scanner_wait_for_positioning error: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ if (action == SA_SCAN)
+ status = artec48u_setup_scan (s, request, action, SANE_FALSE, params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_device_setup_scan failed: %s\n", function_name,
+ sane_strstatus (status)));
+ return status;
+ }
+ status = artec48u_line_reader_new (s->dev, params, &s->reader);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_line_reader_new failed: %s\n", function_name,
+ sane_strstatus (status)));
+ return status;
+ }
+
+ status = artec48u_scanner_internal_start_scan (s);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "%s: artec48u_scanner_internal_start_scan failed: %s\n",
+ function_name, sane_strstatus (status)));
+ return status;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+artec48u_scanner_start_scan (Artec48U_Scanner * s,
+ Artec48U_Scan_Request * request,
+ Artec48U_Scan_Parameters * params)
+{
+ return artec48u_scanner_start_scan_extended (s, request, SA_SCAN, params);
+}
+
+
+static SANE_Status
+artec48u_scanner_stop_scan (Artec48U_Scanner * s)
+{
+ XDBG ((1, "artec48u_scanner_stop_scan begin: \n"));
+ artec48u_line_reader_free (s->reader);
+ s->reader = NULL;
+
+ return artec48u_stop_scan (s->dev);
+}
+
+static void
+calculateGamma (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[0][i] = gval;
+ }
+}
+
+static void
+calculateGammaRed (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA_R].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[1][i] = gval;
+ }
+}
+
+static void
+calculateGammaGreen (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA_G].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[2][i] = gval;
+ }
+}
+
+static void
+calculateGammaBlue (Artec48U_Scanner * s)
+{
+ double d;
+ int gval;
+ unsigned int i;
+
+ double gamma = SANE_UNFIX (s->val[OPT_GAMMA_B].w);
+
+ d = 65536.0 / pow (65536.0, 1.0 / gamma);
+ for (i = 0; i < 65536; i++)
+ {
+ gval = (int) (pow ((double) i, 1.0 / gamma) * d);
+ s->gamma_array[3][i] = gval;
+ }
+}
+
+static SANE_Status
+artec48u_calculate_shading_buffer (Artec48U_Scanner * s, int start, int end,
+ int resolution, SANE_Bool color)
+{
+ int i;
+ int c;
+ int bpp;
+ c = 0;
+ bpp = 6;
+ switch (resolution)
+ {
+ case 50:
+ bpp = 72;
+ break;
+ case 100:
+ bpp = 36;
+ break;
+ case 200:
+ bpp = 18;
+ break;
+ case 300:
+ bpp = 12;
+ break;
+ case 600:
+ bpp = 6;
+ break;
+ case 1200:
+ if(s->dev->is_epro == 0)
+ bpp = 6;
+ else
+ bpp = 3;
+ }
+
+ for (i = start * bpp; i < end * bpp; i += bpp)
+ {
+ if (color)
+ {
+ s->shading_buffer_white[0][c] =
+ (unsigned int) s->shading_buffer_w[i] +
+ ((((unsigned int) s->shading_buffer_w[i + 1]) << 8));
+ s->shading_buffer_white[2][c] =
+ (unsigned int) s->shading_buffer_w[i + 4] +
+ ((((unsigned int) s->shading_buffer_w[i + 5]) << 8));
+ s->shading_buffer_black[0][c] =
+ (unsigned int) s->shading_buffer_b[i] +
+ ((((unsigned int) s->shading_buffer_b[i + 1]) << 8));
+ s->shading_buffer_black[2][c] =
+ (unsigned int) s->shading_buffer_b[i + 4] +
+ ((((unsigned int) s->shading_buffer_b[i + 5]) << 8));
+ }
+ s->shading_buffer_white[1][c] =
+ (unsigned int) s->shading_buffer_w[i + 2] +
+ ((((unsigned int) s->shading_buffer_w[i + 3]) << 8));
+ s->shading_buffer_black[1][c] =
+ (unsigned int) s->shading_buffer_b[i + 2] +
+ ((((unsigned int) s->shading_buffer_b[i + 3]) << 8));
+ ++c;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+init_options (Artec48U_Scanner * s)
+{
+ int i;
+
+ XDBG ((5, "init_options: scanner %p\n", (void *) s));
+ XDBG ((5, "init_options: start\n"));
+ XDBG ((5, "init_options: num options %i\n", NUM_OPTIONS));
+
+ memset (s->val, 0, sizeof (s->val));
+ memset (s->opt, 0, sizeof (s->opt));
+
+ for (i = 0; i < NUM_OPTIONS; ++i)
+ {
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ s->opt[OPT_MODE_GROUP].name = "scanmode-group";
+ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+
+ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_MODE].constraint.string_list = mode_list;
+ s->val[OPT_SCAN_MODE].s = strdup (mode_list[1]);
+
+ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
+ s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
+ s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list;
+ s->val[OPT_BIT_DEPTH].w = bitdepth_list[1];
+
+ /* black level (lineart only) */
+ s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL;
+ s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
+ s->opt[OPT_BLACK_LEVEL].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BLACK_LEVEL].constraint.range = &blacklevel_range;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_BLACK_LEVEL].w = 127;
+
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = resbit_list;
+ s->val[OPT_RESOLUTION].w = resbit_list[1];
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].name = "enhancement-group";
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+
+ /* brightness */
+ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_contrast_range;
+ s->val[OPT_BRIGHTNESS].w = 0;
+
+ /* contrast */
+ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_CONTRAST].constraint.range = &brightness_contrast_range;
+ s->val[OPT_CONTRAST].w = 0;
+
+ /* master analog gamma */
+ s->opt[OPT_GAMMA].name = SANE_NAME_ANALOG_GAMMA;
+ s->opt[OPT_GAMMA].title = SANE_TITLE_ANALOG_GAMMA;
+ s->opt[OPT_GAMMA].desc = SANE_DESC_ANALOG_GAMMA;
+ s->opt[OPT_GAMMA].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA].w = SANE_FIX (s->dev->gamma_master);
+ s->opt[OPT_GAMMA].size = sizeof (SANE_Word);
+
+ /* red analog gamma */
+ s->opt[OPT_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ s->opt[OPT_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ s->opt[OPT_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ s->opt[OPT_GAMMA_R].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_R].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_R].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA_R].w = SANE_FIX (s->dev->gamma_r);
+
+ /* green analog gamma */
+ s->opt[OPT_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ s->opt[OPT_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ s->opt[OPT_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ s->opt[OPT_GAMMA_G].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_G].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_G].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA_G].w = SANE_FIX (s->dev->gamma_g);
+
+ /* blue analog gamma */
+ s->opt[OPT_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ s->opt[OPT_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ s->opt[OPT_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ s->opt[OPT_GAMMA_B].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_B].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_B].constraint.range = &gamma_range;
+ s->val[OPT_GAMMA_B].w = SANE_FIX (s->dev->gamma_b);
+
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].name = "default-enhancements";
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].title = SANE_I18N ("Defaults");
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].desc =
+ SANE_I18N ("Set default values for enhancement controls.");
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].size = 0;
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].type = SANE_TYPE_BUTTON;
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].unit = SANE_UNIT_NONE;
+ s->opt[OPT_DEFAULT_ENHANCEMENTS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].name = "geometry-group";
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = 0;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &scan_range_x;
+ s->val[OPT_TL_X].w = SANE_FIX (0.0);
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &scan_range_y;
+ s->val[OPT_TL_Y].w = SANE_FIX (0.0);
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &scan_range_x;
+ s->val[OPT_BR_X].w = SANE_FIX (50.0);
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &scan_range_y;
+ s->val[OPT_BR_Y].w = SANE_FIX (50.0);
+
+ /* "Calibration" group: */
+ s->opt[OPT_CALIBRATION_GROUP].name = "calibration-group";
+ s->opt[OPT_CALIBRATION_GROUP].title = SANE_I18N ("Calibration");
+ s->opt[OPT_CALIBRATION_GROUP].desc = "";
+ s->opt[OPT_CALIBRATION_GROUP].size = 0;
+ s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_CALIBRATION_GROUP].cap = 0;
+
+ /* calibrate */
+ s->opt[OPT_CALIBRATE].name = "calibration";
+ s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate before next scan");
+ s->opt[OPT_CALIBRATE].desc =
+ SANE_I18N ("If enabled, the device will be calibrated before the "
+ "next scan. Otherwise, calibration is performed "
+ "only before the first start.");
+ s->opt[OPT_CALIBRATE].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CALIBRATE].w = SANE_FALSE;
+
+ /* calibrate */
+ s->opt[OPT_CALIBRATE_SHADING].name = "calibration-shading";
+ s->opt[OPT_CALIBRATE_SHADING].title =
+ SANE_I18N ("Only perform shading-correction");
+ s->opt[OPT_CALIBRATE_SHADING].desc =
+ SANE_I18N ("If enabled, only the shading correction is "
+ "performed during calibration. The default values "
+ "for gain, offset and exposure time, "
+ "either build-in or from the configuration file, "
+ "are used.");
+ s->opt[OPT_CALIBRATE_SHADING].type = SANE_TYPE_BOOL;
+ s->opt[OPT_CALIBRATE_SHADING].unit = SANE_UNIT_NONE;
+ s->opt[OPT_CALIBRATE_SHADING].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_CALIBRATE_SHADING].w = SANE_FALSE;
+#ifdef ARTEC48U_USE_BUTTONS
+ s->opt[OPT_BUTTON_STATE].name = "button-state";
+ s->opt[OPT_BUTTON_STATE].title = SANE_I18N ("Button state");
+ s->opt[OPT_BUTTON_STATE].type = SANE_TYPE_INT;
+ s->opt[OPT_BUTTON_STATE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_BUTTON_STATE].constraint_type = SANE_CONSTRAINT_NONE;
+ s->opt[OPT_BUTTON_STATE].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_BUTTON_STATE].w = 0;
+#endif
+ return SANE_STATUS_GOOD;
+}
+
+static void
+calculate_brightness (Artec48U_Scanner * s)
+{
+ long cnt;
+ double bright;
+
+ bright = (double) s->val[OPT_BRIGHTNESS].w;
+
+ bright *= 257.0;
+ for (cnt = 0; cnt < 65536; cnt++)
+ {
+ if (bright < 0.0)
+ s->brightness_array[cnt] =
+ (int) (((double) cnt * (65535.0 + bright)) / 65535.0);
+ else
+ s->brightness_array[cnt] =
+ (int) ((double) cnt +
+ ((65535.0 - (double) cnt) * bright) / 65535.0);
+ if (s->brightness_array[cnt] > 65535)
+ s->brightness_array[cnt] = 65535;
+ if (s->brightness_array[cnt] < 0)
+ s->brightness_array[cnt] = 0;
+ }
+}
+
+static void
+calculate_contrast (Artec48U_Scanner * s)
+{
+ int val;
+ double p;
+ int cnt;
+ double contr;
+
+ contr = (double) s->val[OPT_CONTRAST].w;
+
+ contr *= 257.0;
+
+ for (cnt = 0; cnt < 65536; cnt++)
+ {
+ if (contr < 0.0)
+ {
+ val = (int) (cnt > 32769) ? (65535 - cnt) : cnt;
+ val = (int) (32769.0 * pow ((double) (val ? val : 1) / 32769.0,
+ (32769.0 + contr) / 32769.0));
+ s->contrast_array[cnt] = (cnt > 32769) ? (65535 - val) : val;
+ if (s->contrast_array[cnt] > 65535)
+ s->contrast_array[cnt] = 65535;
+ if (s->contrast_array[cnt] < 0)
+ s->contrast_array[cnt] = 0;
+ }
+ else
+ {
+ val = (cnt > 32769) ? (65535 - cnt) : cnt;
+ p = ((int) contr == 32769) ? 32769.0 : 32769.0 / (32769.0 - contr);
+ val = (int) (32769.0 * pow ((double) val / 32769.0, p));
+ s->contrast_array[cnt] = (cnt > 32639) ? (65535 - val) : val;
+ if (s->contrast_array[cnt] > 65535)
+ s->contrast_array[cnt] = 65535;
+ if (s->contrast_array[cnt] < 0)
+ s->contrast_array[cnt] = 0;
+ }
+ }
+}
+
+/*
+ The calibration function
+ Disclaimer: the following might be complete crap :-)
+ -Gain, offset, exposure time
+ It seems, that the gain values are actually constants. The windows driver always
+ uses the values 0x0a,0x03,0x03, during calibration as well as during a normal
+ scan. The exposure values are set to 0x04 for black calibration. It's not necessary to
+ move the scan head during this stage.
+ Calibration starts with default values for offset/exposure. These values are
+ increased/decreased until the white and black values are within a specific range, defined
+ by WHITE_MIN, WHITE_MAX, BLACK_MIN and BLACK_MAX.
+
+ -White shading correction
+ The scanning head is moved some lines over the calibration strip. Some lines
+ are scanned at 600dpi/16bit over the full width. The average values are used for the
+ shading buffer. The normal exposure values are used.
+ -Black shading correction
+ Works like the white shading correction, with the difference, that the red-, green-
+ and blue exposure time is set to 0x04 (the value is taken from the windoze driver).
+ -Since we do this over the whole width of the image with the maximal optical resolution,
+ we can use the shading data for every scan, independend of the size, position or resolution,
+ because we have the shading values for every sensor/LED.
+
+ Note:
+ For a CIS device, it's sufficient to determine those values once. It's not necessary, to
+ repeat the calibration sequence before every new scan. The windoze driver even saves the values
+ to various files to avoid the quite lengthy calibration sequence. This backend can also save
+ the values to files. For this purpose, the user has to create a hidden directory called
+ .artec-eplus48u in his/her home directory. If the user insists on calibration
+ before every new scan, he/she can enable a specific option in the backend.
+*/
+static SANE_Status
+calibrate_scanner (SANE_Handle handle)
+{
+ Artec48U_Scanner *s = handle;
+ unsigned int *buffer_pointers[3];
+ int avg_black[3];
+ int avg_white[3];
+ int exp_off;
+ int c;
+ int finish = 0;
+ int noloop = 0;
+
+
+ if ((s->val[OPT_CALIBRATE].w == SANE_TRUE) &&
+ (s->val[OPT_CALIBRATE_SHADING].w == SANE_FALSE))
+ {
+ while (finish == 0)
+ {
+ finish = 1;
+ /*get black values */
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_BLACK,
+ SANE_FALSE, &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_OFFSET_1,
+ &(s->params));
+
+ for (c = 0; c < s->dev->shading_lines_b; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ /* we abuse the shading buffer for the offset calculation */
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_offset_buffer (s, &avg_black[0], &avg_black[1],
+ &avg_black[2]);
+ s->scanning = SANE_FALSE;
+ XDBG ((1, "avg_r: %i, avg_g: %i, avg_b: %i\n", avg_black[0],
+ avg_black[1], avg_black[2]));
+ /*adjust offset */
+ for (c = 0; c < 3; c++)
+ {
+ if (c == 0)
+ {
+ if (avg_black[c] < BLACK_MIN)
+ {
+ s->dev->afe_params.r_offset -= 1;
+ finish = 0;
+ XDBG ((1, "adjust offset r: -1\n"));
+ }
+ else if (avg_black[c] > BLACK_MAX)
+ {
+ s->dev->afe_params.r_offset += 1;
+ finish = 0;
+ XDBG ((1, "adjust offset r: +1\n"));
+ }
+ }
+ if (c == 1)
+ {
+ if (avg_black[c] < BLACK_MIN)
+ {
+ s->dev->afe_params.g_offset -= 1;
+ finish = 0;
+ XDBG ((1, "adjust offset g: -1\n"));
+ }
+ else if (avg_black[c] > BLACK_MAX)
+ {
+ s->dev->afe_params.g_offset += 1;
+ finish = 0;
+ XDBG ((1, "adjust offset g: +1\n"));
+ }
+ }
+ if (c == 2)
+ {
+ if (avg_black[c] < BLACK_MIN)
+ {
+ s->dev->afe_params.b_offset -= 1;
+ finish = 0;
+ XDBG ((1, "adjust offset b: -1\n"));
+ }
+ else if (avg_black[c] > BLACK_MAX)
+ {
+ s->dev->afe_params.b_offset += 1;
+ finish = 0;
+ XDBG ((1, "adjust offset b: +1\n"));
+ }
+ }
+ }
+
+ /*adjust exposure */
+ /*get white values */
+
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_WHITE,
+ SANE_FALSE, &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_EXPOSURE_1,
+ &(s->params));
+
+ for (c = 0; c < s->dev->shading_lines_w; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ /* we abuse the shading buffer for the exposure calculation */
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_exposure_buffer (s, &avg_white[0], &avg_white[1],
+ &avg_white[2]);
+ s->scanning = SANE_FALSE;
+ XDBG ((1, "avg_r: %i, avg_g: %i, avg_b: %i\n", avg_white[0],
+ avg_white[1], avg_white[2]));
+ for (c = 0; c < 3; c++)
+ {
+ if (c == 0)
+ {
+ if (avg_white[c] < WHITE_MIN)
+ {
+ exp_off =
+ ((WHITE_MAX + WHITE_MIN) / 2 -
+ avg_white[c]) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.r_time += exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure r: ++\n"));
+ }
+ else if (avg_white[c] > WHITE_MAX)
+ {
+ exp_off =
+ (avg_white[c] -
+ (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.r_time -= exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure r: --\n"));
+ }
+ }
+ else if (c == 1)
+ {
+ if (avg_white[c] < WHITE_MIN)
+ {
+ exp_off =
+ ((WHITE_MAX + WHITE_MIN) / 2 -
+ avg_white[c]) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.g_time += exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure g: ++\n"));
+ }
+ else if (avg_white[c] > WHITE_MAX)
+ {
+ exp_off =
+ (avg_white[c] -
+ (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.g_time -= exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure g: --\n"));
+ }
+ }
+ else if (c == 2)
+ {
+ if (avg_white[c] < WHITE_MIN)
+ {
+ exp_off =
+ ((WHITE_MAX + WHITE_MIN) / 2 -
+ avg_white[c]) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.b_time += exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure b: ++\n"));
+ }
+ else if (avg_white[c] > WHITE_MAX)
+ {
+ exp_off =
+ (avg_white[c] -
+ (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP;
+ if (exp_off < 1)
+ exp_off = 1;
+ s->dev->exp_params.b_time -= exp_off;
+ finish = 0;
+ XDBG ((1, "adjust exposure b: --\n"));
+ }
+ }
+ }
+
+ XDBG ((1, "time_r: %x, time_g: %x, time_b: %x\n",
+ s->dev->exp_params.r_time, s->dev->exp_params.g_time,
+ s->dev->exp_params.b_time));
+ XDBG ((1, "offset_r: %x, offset_g: %x, offset_b: %x\n",
+ s->dev->afe_params.r_offset, s->dev->afe_params.g_offset,
+ s->dev->afe_params.b_offset));
+ ++noloop;
+ if (noloop > 10)
+ break;
+ }
+ }
+
+ XDBG ((1, "option redOffset 0x%x\n", s->dev->afe_params.r_offset));
+ XDBG ((1, "option greenOffset 0x%x\n", s->dev->afe_params.g_offset));
+ XDBG ((1, "option blueOffset 0x%x\n", s->dev->afe_params.b_offset));
+ XDBG ((1, "option redExposure 0x%x\n", s->dev->exp_params.r_time));
+ XDBG ((1, "option greenExposure 0x%x\n", s->dev->exp_params.g_time));
+ XDBG ((1, "option blueExposure 0x%x\n", s->dev->exp_params.b_time));
+
+ s->dev->artec_48u_afe_params.r_offset = s->dev->afe_params.r_offset;
+ s->dev->artec_48u_afe_params.g_offset = s->dev->afe_params.g_offset;
+ s->dev->artec_48u_afe_params.b_offset = s->dev->afe_params.b_offset;
+ /*don't forget the gain */
+ s->dev->artec_48u_afe_params.r_pga = s->dev->afe_params.r_pga;
+ s->dev->artec_48u_afe_params.g_pga = s->dev->afe_params.g_pga;
+ s->dev->artec_48u_afe_params.b_pga = s->dev->afe_params.b_pga;
+
+ s->dev->artec_48u_exposure_params.r_time = s->dev->exp_params.r_time;
+ s->dev->artec_48u_exposure_params.g_time = s->dev->exp_params.g_time;
+ s->dev->artec_48u_exposure_params.b_time = s->dev->exp_params.b_time;
+
+ /*******************************
+ *get the black shading values *
+ *******************************/
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_BLACK, SANE_FALSE,
+ &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_BLACK,
+ &(s->params));
+
+ for (c = 0; c < s->dev->shading_lines_b; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_shading_buffer (s, SANE_FALSE);
+ s->scanning = SANE_FALSE;
+
+ /*******************************
+ *get the white shading values *
+ *******************************/
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+ s->scanning = SANE_TRUE;
+
+ init_shading_buffer (s);
+
+ artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_WHITE, SANE_FALSE,
+ &(s->params));
+ artec48u_scanner_start_scan_extended (s, &(s->request),
+ SA_CALIBRATE_SCAN_WHITE,
+ &(s->params));
+ for (c = 0; c < s->dev->shading_lines_w; c++)
+ {
+ artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE);
+ add_to_shading_buffer (s, buffer_pointers);
+ }
+ artec48u_scanner_stop_scan (s);
+ finish_shading_buffer (s, SANE_TRUE);
+ s->scanning = SANE_FALSE;
+ save_calibration_data (s);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+close_pipe (Artec48U_Scanner * s)
+{
+ if (s->pipe >= 0)
+ {
+ XDBG ((1, "close_pipe\n"));
+ close (s->pipe);
+ s->pipe = -1;
+ }
+ return SANE_STATUS_EOF;
+}
+static RETSIGTYPE
+sigalarm_handler (int signal)
+{
+ int dummy; /*Henning doesn't like warnings :-) */
+ XDBG ((1, "ALARM!!!\n"));
+ dummy = signal;
+ cancelRead = SANE_TRUE;
+}
+
+static void
+sig_chldhandler (int signo)
+{
+ XDBG ((1, "Child is down (signal=%d)\n", signo));
+}
+
+static int
+reader_process (void * data)
+{
+ Artec48U_Scanner * s = (Artec48U_Scanner *) data;
+ int fd = s->reader_pipe;
+
+ SANE_Status status;
+ struct SIGACTION act;
+ sigset_t ignore_set;
+ ssize_t bytes_written = 0;
+
+ XDBG ((1, "reader process...\n"));
+
+ if (sanei_thread_is_forked()) close (s->pipe);
+
+ sigfillset (&ignore_set);
+ sigdelset (&ignore_set, SIGTERM);
+ sigdelset (&ignore_set, SIGUSR1);
+#if defined (__APPLE__) && defined (__MACH__)
+ sigdelset (&ignore_set, SIGUSR2);
+#endif
+ sigprocmask (SIG_SETMASK, &ignore_set, 0);
+
+ memset (&act, 0, sizeof (act));
+ sigaction (SIGTERM, &act, 0);
+ sigaction (SIGUSR1, &act, 0);
+
+ cancelRead = SANE_FALSE;
+ if (sigemptyset (&(act.sa_mask)) < 0)
+ XDBG ((2, "(child) reader_process: sigemptyset() failed\n"));
+ act.sa_flags = 0;
+
+ act.sa_handler = reader_process_sigterm_handler;
+ if (sigaction (SIGTERM, &act, 0) < 0)
+ XDBG ((2, "(child) reader_process: sigaction(SIGTERM,...) failed\n"));
+
+ act.sa_handler = usb_reader_process_sigterm_handler;
+ if (sigaction (SIGUSR1, &act, 0) < 0)
+ XDBG ((2, "(child) reader_process: sigaction(SIGUSR1,...) failed\n"));
+
+
+ XDBG ((2, "(child) reader_process: s=%p, fd=%d\n", (void *) s, fd));
+
+ /*read line by line into buffer */
+ /*copy buffer pointers to line_buffer */
+ XDBG ((2, "(child) reader_process: byte_cnt %d\n", (int) s->byte_cnt));
+ s->eof = SANE_FALSE;
+ while (s->lines_to_read > 0)
+ {
+ if (cancelRead == SANE_TRUE)
+ {
+ XDBG ((2, "(child) reader_process: cancelRead == SANE_TRUE\n"));
+ s->scanning = SANE_FALSE;
+ s->eof = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+ if (s->scanning != SANE_TRUE)
+ {
+ XDBG ((2, "(child) reader_process: scanning != SANE_TRUE\n"));
+ return SANE_STATUS_CANCELLED;
+ }
+ status = artec48u_scanner_read_line (s, s->buffer_pointers, SANE_TRUE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "(child) reader_process: scanner_read_line failed\n"));
+ return SANE_STATUS_IO_ERROR;
+ }
+ copy_scan_line (s);
+ s->lines_to_read -= 1;
+ bytes_written =
+ write (fd, s->line_buffer, s->sane_params.bytes_per_line);
+
+ if (bytes_written < 0)
+ {
+ XDBG ((2, "(child) reader_process: write returned %s\n",
+ strerror (errno)));
+ s->eof = SANE_FALSE;
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ XDBG ((2, "(child) reader_process: lines to read %i\n", s->lines_to_read));
+ }
+ s->eof = SANE_TRUE;
+ close (fd);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+do_cancel (Artec48U_Scanner * s, SANE_Bool closepipe)
+{
+ struct SIGACTION act;
+ SANE_Pid res;
+ XDBG ((1, "do_cancel\n"));
+
+ s->scanning = SANE_FALSE;
+
+ if (s->reader_pid != -1)
+ {
+ /*parent */
+ XDBG ((1, "killing reader_process\n"));
+ /* tell the driver to stop scanning */
+ sigemptyset (&(act.sa_mask));
+ act.sa_flags = 0;
+
+ act.sa_handler = sigalarm_handler;
+
+ if (sigaction (SIGALRM, &act, 0) == -1)
+ XDBG ((1, "sigaction() failed !\n"));
+
+ /* kill our child process and wait until done */
+ alarm (10);
+ if (sanei_thread_kill (s->reader_pid) < 0)
+ XDBG ((1, "sanei_thread_kill() failed !\n"));
+ res = sanei_thread_waitpid (s->reader_pid, 0);
+ alarm (0);
+
+ if (res != s->reader_pid)
+ {
+ XDBG ((1, "sanei_thread_waitpid() failed !\n"));
+ }
+ s->reader_pid = -1;
+ XDBG ((1, "reader_process killed\n"));
+ }
+ if (SANE_TRUE == closepipe)
+ {
+ close_pipe (s);
+ XDBG ((1, "pipe closed\n"));
+ }
+ artec48u_scanner_stop_scan (s);
+ artec48u_carriage_home (s->dev);
+ if (s->line_buffer)
+ {
+ XDBG ((2, "freeing line_buffer\n"));
+ free (s->line_buffer);
+ s->line_buffer = NULL;
+ }
+ if (s->lineart_buffer)
+ {
+ XDBG ((2, "freeing lineart_buffer\n"));
+ free (s->lineart_buffer);
+ s->lineart_buffer = NULL;
+ }
+
+ return SANE_STATUS_CANCELLED;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ Artec48U_Device *dev;
+ SANE_Int dev_num;
+
+ XDBG ((5, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false"));
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ dev_num = 0;
+ for (dev = first_dev; dev_num < num_devices; dev = dev->next)
+ {
+ devlist[dev_num] = &dev->sane;
+ XDBG ((3, "sane_get_devices: name %s\n", dev->sane.name));
+ XDBG ((3, "sane_get_devices: vendor %s\n", dev->sane.vendor));
+ XDBG ((3, "sane_get_devices: model %s\n", dev->sane.model));
+ ++dev_num;
+ }
+ devlist[dev_num] = 0;
+ ++dev_num;
+
+ *device_list = devlist;
+
+ XDBG ((5, "sane_get_devices: exit\n"));
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+load_calibration_data (Artec48U_Scanner * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ FILE *f = 0;
+ size_t cnt;
+ char path[PATH_MAX];
+ char filename[PATH_MAX];
+
+ s->calibrated = SANE_FALSE;
+ path[0] = 0;
+ if (strlen (getenv ("HOME")) < (PATH_MAX - 1))
+ strcat (path, getenv ("HOME"));
+ else
+ return SANE_STATUS_INVAL;
+
+ if (strlen (path) < (PATH_MAX - 1 - strlen ("/.artec_eplus48u/")))
+ strcat (path, "/.artec_eplus48u/");
+ else
+ return SANE_STATUS_INVAL;
+
+ /*try to load black shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_black")))
+ strcat (filename, "artec48ushading_black");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read black shading file: \"%s\"\n", filename));
+
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+
+ /*read values */
+ cnt = fread (s->shading_buffer_b, sizeof (unsigned char), 30720*s->dev->epro_mult, f); /*epro*/
+ if (cnt != (30720*s->dev->epro_mult)) /*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not load black shading file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to load white shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_white")))
+ strcat (filename, "artec48ushading_white");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read white shading file: \"%s\"\n", filename));
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt = fread (s->shading_buffer_w, sizeof (unsigned char), 30720*s->dev->epro_mult, f);/*epro*/
+ if (cnt != (30720*s->dev->epro_mult)) /*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not load white shading file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to load offset file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uoffset")))
+ strcat (filename, "artec48uoffset");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read offset file: \"%s\"\n", filename));
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fread (&s->dev->artec_48u_afe_params, sizeof (Artec48U_AFE_Parameters), 1,
+ f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not load offset file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*load exposure file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uexposure")))
+ strcat (filename, "artec48uexposure");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to read exposure file: \"%s\"\n", filename));
+ f = fopen (filename, "rb");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fread (&s->dev->artec_48u_exposure_params,
+ sizeof (Artec48U_Exposure_Parameters), 1, f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not load exposure file\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+ s->calibrated = SANE_TRUE;
+ return status;
+}
+
+static SANE_Status
+save_calibration_data (Artec48U_Scanner * s)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ FILE *f = 0;
+ size_t cnt;
+ char path[PATH_MAX];
+ char filename[PATH_MAX];
+ mode_t mode = S_IRUSR | S_IWUSR;
+
+ path[0] = 0;
+ if (strlen (getenv ("HOME")) < (PATH_MAX - 1))
+ strcat (path, getenv ("HOME"));
+ else
+ return SANE_STATUS_INVAL;
+
+ if (strlen (path) < (PATH_MAX - 1 - strlen ("/.artec_eplus48u/")))
+ strcat (path, "/.artec_eplus48u/");
+ else
+ return SANE_STATUS_INVAL;
+
+ /*try to save black shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_black")))
+ strcat (filename, "artec48ushading_black");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to save black shading file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ {
+ XDBG ((1, "Could not save artec48ushading_black\n"));
+ return SANE_STATUS_INVAL;
+ }
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+
+ /*read values */
+ cnt = fwrite (s->shading_buffer_b, sizeof (unsigned char), 30720*s->dev->epro_mult, f); /*epro*/
+ XDBG ((1, "Wrote %li bytes to black shading buffer \n", (u_long) cnt));
+ if (cnt != (30720*s->dev->epro_mult))/*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not write black shading buffer\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to save white shading file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_white")))
+ strcat (filename, "artec48ushading_white");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to save white shading file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt = fwrite (s->shading_buffer_w, sizeof (unsigned char), 30720*s->dev->epro_mult, f);/*epro*/
+ if (cnt != (30720*s->dev->epro_mult)) /*epro*/
+ {
+ fclose (f);
+ XDBG ((1, "Could not write white shading buffer\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to save offset file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uoffset")))
+ strcat (filename, "artec48uoffset");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to write offset file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fwrite (&s->dev->artec_48u_afe_params, sizeof (Artec48U_AFE_Parameters),
+ 1, f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not write afe values\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+
+ /*try to write exposure file */
+ strcpy (filename, path);
+ if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uexposure")))
+ strcat (filename, "artec48uexposure");
+ else
+ return SANE_STATUS_INVAL;
+ XDBG ((1, "Try to write exposure file: \"%s\"\n", filename));
+ f = fopen (filename, "w");
+ if (!f)
+ return SANE_STATUS_INVAL;
+ if (chmod (filename, mode) != 0)
+ return SANE_STATUS_INVAL;
+ /*read values */
+ cnt =
+ fwrite (&s->dev->artec_48u_exposure_params,
+ sizeof (Artec48U_Exposure_Parameters), 1, f);
+ if (cnt != 1)
+ {
+ fclose (f);
+ XDBG ((1, "Could not write exposure values\n"));
+ return SANE_STATUS_INVAL;
+ }
+ fclose (f);
+ return status;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status = SANE_STATUS_INVAL;
+ Artec48U_Device *dev = 0;
+ Artec48U_Scanner *s = 0;
+
+ if (!devicename)
+ return SANE_STATUS_INVAL;
+ XDBG ((2, "sane_open: devicename = \"%s\"\n", devicename));
+
+
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ XDBG ((2, "sane_open: found matching device %s\n",
+ dev->sane.name));
+ break;
+ }
+ }
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ XDBG ((2, "sane_open: attach failed %s\n", devicename));
+ }
+ }
+ else
+ {
+ /* empty devicename -> use first device */
+ XDBG ((2, "sane_open: empty devicename\n"));
+ dev = first_dev;
+ }
+ if (!dev)
+ return SANE_STATUS_INVAL;
+
+ status = artec48u_device_open (dev);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "could not open device\n"));
+ return status;
+ }
+ XDBG ((2, "sane_open: opening device `%s', handle = %p\n", dev->sane.name,
+ (void *) dev));
+
+ XDBG ((1, "sane_open - %s\n", dev->sane.name));
+ XDBG ((2, "sane_open: try to open %s\n", dev->sane.name));
+
+ status = artec48u_device_activate (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "could not activate device\n"));
+ return status;
+ }
+ /* We do not check anymore, whether the firmware is already loaded */
+ /* because that caused problems after rebooting; furthermore, loading */
+ /* of the firmware is fast, therefore the test doesn't make much sense */
+ status = download_firmware_file (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((3, "download_firmware_file failed\n"));
+ return status;
+ }
+ /* If a scan is interrupted without sending stop_scan, bad things happen.
+ * Send the stop scan command now just in case. */
+ artec48u_stop_scan (dev);
+
+ artec48u_wait_for_positioning (dev);
+
+ artec48u_scanner_new (dev, &s);
+ init_calibrator (s);
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+
+ status = init_options (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ /*Try to load the calibration values */
+ status = load_calibration_data (s);
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Artec48U_Scanner *prev, *s;
+
+ XDBG ((5, "sane_close: start\n"));
+
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ XDBG ((5, "close: invalid handle %p\n", handle));
+ return;
+ }
+ artec48u_device_close (s->dev);
+ artec48u_scanner_free (s);
+ XDBG ((5, "sane_close: exit\n"));
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Artec48U_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ XDBG ((5, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option));
+ return s->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int * info)
+{
+ Artec48U_Scanner *s = handle;
+#ifdef ARTEC48U_USE_BUTTONS
+ SANE_Int button_state;
+#endif
+ SANE_Status status;
+ XDBG ((8, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ (void *) handle, option, action, (void *) value, (void *) info));
+
+ if (info)
+ *info = 0;
+
+ if (option < 0 || option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL; /* Unknown option ... */
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap))
+ return SANE_STATUS_INVAL;
+
+ switch (action)
+ {
+ case SANE_ACTION_SET_VALUE:
+ if (s->scanning == SANE_TRUE)
+ return SANE_STATUS_INVAL;
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap))
+ return SANE_STATUS_INVAL;
+
+ status = sanei_constrain_value (s->opt + option, value, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option)
+ {
+ case OPT_RESOLUTION:
+ if(s->dev->is_epro != 0)
+ {
+ if((s->val[option].w == 1200) && (*(SANE_Word *) value < 1200))
+ {
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ else if((s->val[option].w < 1200) && (*(SANE_Word *) value == 1200))
+ {
+ s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list2;
+ if(s->val[OPT_BIT_DEPTH].w > 8)
+ s->val[OPT_BIT_DEPTH].w = 8;
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ }
+ s->val[option].w = *(SANE_Word *) value;
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+ /* fall through */
+ case OPT_BIT_DEPTH:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) value;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ /* fall through */
+ case OPT_BLACK_LEVEL:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_GAMMA:
+ case OPT_GAMMA_R:
+ case OPT_GAMMA_G:
+ case OPT_GAMMA_B:
+ case OPT_CALIBRATE:
+ case OPT_CALIBRATE_SHADING:
+ s->val[option].w = *(SANE_Word *) value;
+ return SANE_STATUS_GOOD;
+ case OPT_DEFAULT_ENHANCEMENTS:
+ s->val[OPT_GAMMA].w = SANE_FIX (s->dev->gamma_master);
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[2]) == 0)
+ {
+ s->val[OPT_GAMMA_R].w = SANE_FIX (s->dev->gamma_r);
+ s->val[OPT_GAMMA_G].w = SANE_FIX (s->dev->gamma_g);
+ s->val[OPT_GAMMA_B].w = SANE_FIX (s->dev->gamma_b);
+ }
+ s->val[OPT_BRIGHTNESS].w = 0;
+ s->val[OPT_CONTRAST].w = 0;
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_SCAN_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (value);
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0)
+ {
+ s->opt[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
+ }
+ else if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[1]) == 0)
+ {
+ s->opt[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ s->opt[OPT_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
+ s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ }
+ break;
+ case SANE_ACTION_GET_VALUE:
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BIT_DEPTH:
+ case OPT_BLACK_LEVEL:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_GAMMA:
+ case OPT_GAMMA_R:
+ case OPT_GAMMA_G:
+ case OPT_GAMMA_B:
+ case OPT_CALIBRATE:
+ case OPT_CALIBRATE_SHADING:
+ *(SANE_Word *) value = (SANE_Word) s->val[option].w;
+ return SANE_STATUS_GOOD;
+ /* string options: */
+ case OPT_SCAN_MODE:
+ strcpy (value, s->val[option].s);
+ return SANE_STATUS_GOOD;
+#ifdef ARTEC48U_USE_BUTTONS
+ case OPT_BUTTON_STATE:
+ status = artec48u_check_buttons (s->dev, &button_state);
+ if (status == SANE_STATUS_GOOD)
+ {
+ s->val[option].w = button_state;
+ *(SANE_Int *) value = (SANE_Int) s->val[option].w;
+ }
+ else
+ {
+ s->val[option].w = 0;
+ *(SANE_Int *) value = 0;
+ }
+ return SANE_STATUS_GOOD;
+#endif
+ }
+ break;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Artec48U_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word resx;
+/* int scan_mode;*/
+ SANE_String str = s->val[OPT_SCAN_MODE].s;
+ int tlx;
+ int tly;
+ int brx;
+ int bry;
+ int tmp;
+ XDBG ((2, "sane_get_params: string %s\n", str));
+ XDBG ((2, "sane_get_params: enter\n"));
+
+ tlx = s->val[OPT_TL_X].w;
+ tly = s->val[OPT_TL_Y].w;
+ brx = s->val[OPT_BR_X].w;
+ bry = s->val[OPT_BR_Y].w;
+
+ /*make sure, that tlx < brx and tly < bry
+ this will NOT change the options */
+ if (tlx > brx)
+ {
+ tmp = tlx;
+ tlx = brx;
+ brx = tmp;
+ }
+ if (tly > bry)
+ {
+ tmp = tly;
+ tly = bry;
+ bry = tmp;
+ }
+ resx = s->val[OPT_RESOLUTION].w;
+ str = s->val[OPT_SCAN_MODE].s;
+
+ s->request.color = SANE_TRUE;
+ if ((strcmp (str, mode_list[0]) == 0) || (strcmp (str, mode_list[1]) == 0))
+ s->request.color = SANE_FALSE;
+ else
+ s->request.color = SANE_TRUE;
+ s->request.depth = s->val[OPT_BIT_DEPTH].w;
+ if (strcmp (str, mode_list[0]) == 0)
+ s->request.depth = 8;
+ s->request.y0 = tly; /**< Top boundary */
+ s->request.x0 = SANE_FIX (216.0) - brx; /**< left boundary */
+ s->request.xs = brx - tlx; /**< Width */
+ s->request.ys = bry - tly; /**< Height */
+ s->request.xdpi = resx; /**< Horizontal resolution */
+ s->request.ydpi = resx; /**< Vertical resolution */
+ /*epro*/
+ if ((resx == 1200) && (s->dev->is_epro == 0))
+ s->request.xdpi = 600;/**< Vertical resolution */
+
+ status = artec48u_setup_scan (s, &(s->request), SA_SCAN,
+ SANE_TRUE, &(s->params));
+ if (status != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+/*DBG(1, "sane_get_params: scan_mode %i\n",scan_mode);*/
+
+ params->depth = s->params.depth;
+ s->params.lineart = SANE_FALSE;
+ if (s->params.color == SANE_TRUE)
+ {
+ params->format = SANE_FRAME_RGB;
+ params->bytes_per_line = s->params.pixel_xs * 3;
+ }
+ else
+ {
+ params->format = SANE_FRAME_GRAY;
+ params->bytes_per_line = s->params.pixel_xs;
+ if (strcmp (str, mode_list[0]) == 0)
+ {
+ params->depth = 1;
+ params->bytes_per_line = (s->params.pixel_xs + 7) / 8;
+ s->params.lineart = SANE_TRUE;
+ }
+ }
+ if ((resx == 1200) && (s->dev->is_epro == 0))
+ {
+ if (params->depth == 1)
+ params->bytes_per_line = (s->params.pixel_xs * 2 + 7) / 8;
+ else
+ params->bytes_per_line *= 2;
+ }
+ if (params->depth == 16)
+ params->bytes_per_line *= 2;
+ params->last_frame = SANE_TRUE;
+ params->pixels_per_line = s->params.pixel_xs;
+ if ((resx == 1200) && (s->dev->is_epro == 0))
+ params->pixels_per_line *= 2;
+ params->lines = s->params.pixel_ys;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Artec48U_Scanner *s = handle;
+ SANE_Status status;
+ int fds[2];
+
+ if (s->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (sane_get_parameters (handle, &s->sane_params) != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+ if ((s->calibrated != SANE_TRUE) || (s->val[OPT_CALIBRATE].w == SANE_TRUE))
+ {
+ XDBG ((1, "Must calibrate scanner\n"));
+ status = calibrate_scanner (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ s->calibrated = SANE_TRUE;
+ }
+ if (sane_get_parameters (handle, &s->sane_params) != SANE_STATUS_GOOD)
+ return SANE_STATUS_INVAL;
+
+ calculate_brightness (s);
+ calculate_contrast (s);
+ calculateGamma (s);
+ calculateGammaRed (s);
+ calculateGammaGreen (s);
+ calculateGammaBlue (s);
+
+ artec48u_carriage_home (s->dev);
+
+ artec48u_wait_for_positioning (s->dev);
+ s->reader = NULL;
+
+ s->scanning = SANE_TRUE;
+ s->byte_cnt = 0;
+ s->lines_to_read = s->params.pixel_ys;
+ /*allocate a buffer, that can hold a complete scan line */
+ /*If resolution is 1200 dpi and we are scanning in lineart mode,
+ then we also allocate a lineart_buffer, which can hold a complete scan line
+ in 8 bit/gray. This makes interpolation easier. */
+ if ((s->params.ydpi == 1200) && (s->dev->is_epro == 0))
+ {
+ if (s->request.color == SANE_TRUE)
+ {
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 8);
+ }
+ else
+ {
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 4);
+ /*lineart ? */
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0)
+ s->lineart_buffer = (SANE_Byte *) malloc (s->params.pixel_xs * 2);
+ }
+ }
+ else
+ {
+ if (s->request.color == SANE_TRUE)
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 4);
+ else
+ {
+ s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 2);
+ /*lineart ? */
+ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0)
+ s->lineart_buffer = (SANE_Byte *) malloc (s->params.pixel_xs * 2);
+ }
+ }
+ if (pipe (fds) < 0)
+ {
+ s->scanning = SANE_FALSE;
+ XDBG ((2, "sane_start: pipe failed (%s)\n", strerror (errno)));
+ return SANE_STATUS_IO_ERROR;
+ }
+ status = artec48u_scanner_start_scan (s, &s->request, &s->params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ XDBG ((2, "sane_start: could not start scan\n"));
+ return status;
+ }
+ s->pipe = fds[0];
+ s->reader_pipe = fds[1];
+ s->reader_pid = sanei_thread_begin (reader_process, s);
+ cancelRead = SANE_FALSE;
+ if (s->reader_pid == -1)
+ {
+ s->scanning = SANE_FALSE;
+ XDBG ((2, "sane_start: sanei_thread_begin failed (%s)\n", strerror (errno)));
+ return SANE_STATUS_NO_MEM;
+ }
+ signal (SIGCHLD, sig_chldhandler);
+
+ if (sanei_thread_is_forked()) close (s->reader_pipe);
+
+ XDBG ((1, "sane_start done\n"));
+
+ return SANE_STATUS_GOOD; /* parent */
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Artec48U_Scanner *s = handle;
+ ssize_t nread;
+
+ *length = 0;
+
+ /* here we read all data from the driver... */
+ nread = read (s->pipe, data, max_length);
+ XDBG ((3, "sane_read - read %ld bytes\n", (long) nread));
+ if (cancelRead == SANE_TRUE)
+ {
+ return do_cancel (s, SANE_TRUE);
+ }
+
+ if (nread < 0)
+ {
+ if (EAGAIN == errno)
+ {
+ /* if we already had read the picture, so it's okay and stop */
+ if (s->eof == SANE_TRUE)
+ {
+ sanei_thread_waitpid (s->reader_pid, 0);
+ s->reader_pid = -1;
+ artec48u_scanner_stop_scan (s);
+ artec48u_carriage_home (s->dev);
+ return close_pipe (s);
+ }
+ /* else force the frontend to try again */
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ XDBG ((4, "ERROR: errno=%d\n", errno));
+ do_cancel (s, SANE_TRUE);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ *length = nread;
+ s->byte_cnt += nread;
+
+ /* nothing read means that we're finished OR we had a problem... */
+ if (0 == nread)
+ {
+ if (0 == s->byte_cnt)
+ {
+ s->exit_code = sanei_thread_get_status (s->reader_pid);
+
+ if (SANE_STATUS_GOOD != s->exit_code)
+ {
+ close_pipe (s);
+ return s->exit_code;
+ }
+ }
+ return close_pipe (s);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Artec48U_Scanner *s = handle;
+ XDBG ((2, "sane_cancel: handle = %p\n", handle));
+ if (s->scanning)
+ do_cancel (s, SANE_FALSE);
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Artec48U_Scanner *s = (Artec48U_Scanner *) handle;
+
+ XDBG ((1, "sane_set_io_mode: non_blocking=%d\n", non_blocking));
+
+ if (!s->scanning)
+ {
+ XDBG ((4, "ERROR: not scanning !\n"));
+ return SANE_STATUS_INVAL;
+ }
+
+ if (-1 == s->pipe)
+ {
+ XDBG ((4, "ERROR: not supported !\n"));
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
+ {
+ XDBG ((4, "ERROR: can?t set to non-blocking mode !\n"));
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ XDBG ((1, "sane_set_io_mode done\n"));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Artec48U_Scanner *s = (Artec48U_Scanner *) handle;
+
+ XDBG ((1, "sane_get_select_fd\n"));
+
+ if (!s->scanning)
+ {
+ XDBG ((4, "ERROR: not scanning !\n"));
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = s->pipe;
+
+ XDBG ((1, "sane_get_select_fd done\n"));
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ Artec48U_Device *device = 0;
+ SANE_Status status;
+ char str[PATH_MAX] = _DEFAULT_DEVICE;
+ char temp[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ double gamma_m = 1.9;
+ double gamma_r = 1.0;
+ double gamma_g = 1.0;
+ double gamma_b = 1.0;
+ int epro_default = 0;
+
+ DBG_INIT ();
+ eProMult = 1;
+ isEPro = 0;
+ temp[0] = 0;
+ strcpy (vendor_string, "Artec");
+ strcpy (model_string, "E+ 48U");
+
+ sanei_usb_init ();
+ sanei_thread_init ();
+
+ /* do some presettings... */
+ auth = authorize;
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ fp = sanei_config_open (ARTEC48U_CONFIG_FILE);
+
+ /* default to _DEFAULT_DEVICE instead of insisting on config file */
+ if (NULL == fp)
+ {
+ status = attach (_DEFAULT_DEVICE, &device);
+ return status;
+ }
+
+ while (sanei_config_read (str, sizeof (str), fp))
+ {
+ XDBG ((1, "sane_init, >%s<\n", str));
+ /* ignore line comments */
+ if (str[0] == '#')
+ continue;
+ len = strlen (str);
+ /* ignore empty lines */
+ if (0 == len)
+ continue;
+ /* check for options */
+ if (0 == strncmp (str, "option", 6))
+ {
+ if(decodeVal (str,"ePlusPro",_INT, &isEPro,&epro_default) == SANE_TRUE)
+ {
+ eProMult = 1;
+ if(isEPro != 0)
+ {
+ eProMult = 2;
+ XDBG ((3, "Is Artec E Pro\n"));
+ }
+ else
+ XDBG ((3, "Is Artec E+ 48U\n"));
+ }
+ decodeVal (str, "masterGamma", _FLOAT, &gamma_master_default,
+ &gamma_m);
+ decodeVal (str, "redGamma", _FLOAT, &gamma_r_default, &gamma_r);
+ decodeVal (str, "greenGamma", _FLOAT, &gamma_g_default, &gamma_g);
+ decodeVal (str, "blueGamma", _FLOAT, &gamma_b_default, &gamma_b);
+ decodeVal (str, "redOffset", _BYTE, &afe_params.r_offset,
+ &default_afe_params.r_offset);
+ decodeVal (str, "greenOffset", _BYTE, &afe_params.g_offset,
+ &default_afe_params.g_offset);
+ decodeVal (str, "blueOffset", _BYTE, &afe_params.b_offset,
+ &default_afe_params.b_offset);
+
+ decodeVal (str, "redExposure", _INT, &exp_params.r_time,
+ &default_exp_params.r_time);
+ decodeVal (str, "greenExposure", _INT, &exp_params.g_time,
+ &default_exp_params.g_time);
+ decodeVal (str, "blueExposure", _INT, &exp_params.b_time,
+ &default_exp_params.b_time);
+
+ decodeVal (str, "modelString", _STRING, model_string, model_string);
+ decodeVal (str, "vendorString", _STRING, vendor_string,
+ vendor_string);
+
+ decodeVal (str, "artecFirmwareFile", _STRING, firmwarePath,
+ firmwarePath);
+ }
+ else if (0 == strncmp (str, "usb", 3))
+ {
+ if (temp[0] != 0)
+ {
+ XDBG ((3, "trying to attach: %s\n", temp));
+ XDBG ((3, " vendor: %s\n", vendor_string));
+ XDBG ((3, " model: %s\n", model_string));
+ sanei_usb_attach_matching_devices (temp, attach_one_device);
+ }
+ /*save config line in temp */
+ strcpy (temp, str);
+ }
+ else if (0 == strncmp (str, "device", 6))
+ {
+ if (SANE_TRUE == decodeDevName (str, devName))
+ {
+ if (devName[0] != 0)
+ sanei_usb_attach_matching_devices (devName,
+ attach_one_device);
+ temp[0] = 0;
+ }
+ }
+ else
+ {
+ /* ignore other stuff... */
+ XDBG ((1, "ignoring >%s<\n", str));
+ }
+ }
+ if (temp[0] != 0)
+ {
+ XDBG ((3, "trying to attach: %s\n", temp));
+ XDBG ((3, " vendor: %s\n", vendor_string));
+ XDBG ((3, " model: %s\n", model_string));
+ sanei_usb_attach_matching_devices (temp, attach_one_device);
+ temp[0] = 0;
+ }
+
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ Artec48U_Device *dev, *next;
+
+ XDBG ((5, "sane_exit: start\n"));
+ for (dev = first_dev; dev; dev = next)
+ {
+ next = dev->next;
+ /*function will check, whether device is really open */
+ artec48u_device_close (dev);
+ artec48u_device_free (dev);
+ }
+ XDBG ((5, "sane_exit: exit\n"));
+ return;
+}