kopia lustrzana https://gitlab.com/sane-project/backends
4581 wiersze
124 KiB
C
4581 wiersze
124 KiB
C
/* 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 12
|
|
|
|
#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 void
|
|
reader_process_sigterm_handler (int signal)
|
|
{
|
|
XDBG ((1, "reader_process: terminated by signal %d\n", signal));
|
|
_exit (SANE_STATUS_GOOD);
|
|
}
|
|
|
|
static void
|
|
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 void
|
|
sigalarm_handler (int __sane_unused__ signal)
|
|
{
|
|
XDBG ((1, "ALARM!!!\n"));
|
|
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 (sanei_thread_is_valid (s->reader_pid))
|
|
{
|
|
/*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"));
|
|
}
|
|
sanei_thread_invalidate (s->reader_pid);
|
|
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;
|
|
|
|
/* return SANE_STATUS_INVAL if HOME environment variable is not set */
|
|
if (getenv ("HOME") == NULL)
|
|
{
|
|
XDBG ((1, "Environment variable HOME not set\n"));
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
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;
|
|
|
|
/* return SANE_STATUS_INVAL if HOME environment variable is not set */
|
|
if (getenv ("HOME") == NULL)
|
|
{
|
|
XDBG ((1, "Environment variable HOME not set\n"));
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
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 *s;
|
|
|
|
XDBG ((5, "sane_close: start\n"));
|
|
|
|
/* remove handle from list of open handles: */
|
|
for (s = first_handle; s; s = s->next)
|
|
{
|
|
if (s == handle)
|
|
break;
|
|
}
|
|
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 (!sanei_thread_is_valid (s->reader_pid))
|
|
{
|
|
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);
|
|
sanei_thread_invalidate (s->reader_pid);
|
|
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;
|
|
}
|