diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
commit | 6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch) | |
tree | 2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/umax_pp.c |
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/umax_pp.c')
-rw-r--r-- | backend/umax_pp.c | 2404 |
1 files changed, 2404 insertions, 0 deletions
diff --git a/backend/umax_pp.c b/backend/umax_pp.c new file mode 100644 index 0000000..90cd486 --- /dev/null +++ b/backend/umax_pp.c @@ -0,0 +1,2404 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2001-2012 Stéphane Voltz <stef.dev@free.fr> + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. + + This file implements a SANE backend for Umax PP flatbed scanners. */ + +/* CREDITS: + Started by being a mere copy of mustek_pp + by Jochen Eisinger <jochen.eisinger@gmx.net> + then evolved in its own thing + + support for the 610P has been made possible thank to an hardware donation + from William Stuart + */ + + +#include "../include/sane/config.h" + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <math.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#define DEBUG_NOT_STATIC + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_config.h" + +#define BACKEND_NAME umax_pp +#include "../include/sane/sanei_backend.h" + +#include "umax_pp_mid.h" +#include "umax_pp.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define UMAX_PP_CONFIG_FILE "umax_pp.conf" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* DEBUG + * for debug output, set SANE_DEBUG_UMAX_PP to + * 0 for nothing + * 1 for errors + * 2 for warnings + * 3 for additional information + * 4 for debug information + * 5 for code flow protocol (there isn't any) + * 129 if you want to know which parameters are unused + */ + +/* history: + * see Changelog + */ + +#define UMAX_PP_BUILD 2301 +#define UMAX_PP_STATE "release" + +static int num_devices = 0; +static Umax_PP_Descriptor *devlist = NULL; +static const SANE_Device **devarray = NULL; + +static Umax_PP_Device *first_dev = NULL; + + +/* 2 Meg scan buffer */ +static SANE_Word buf_size = 2048 * 1024; + +static SANE_Word red_gain = 0; +static SANE_Word green_gain = 0; +static SANE_Word blue_gain = 0; + +static SANE_Word red_offset = 0; +static SANE_Word green_offset = 0; +static SANE_Word blue_offset = 0; +static SANE_Char scanner_vendor[128]=""; +static SANE_Char scanner_name[128]=""; +static SANE_Char scanner_model[128]=""; +static SANE_Char astra[128]; + + + +static const SANE_String_Const mode_list[] = { + SANE_VALUE_SCAN_MODE_LINEART, + SANE_VALUE_SCAN_MODE_GRAY, + SANE_VALUE_SCAN_MODE_COLOR, + 0 +}; + +static const SANE_Range u4_range = { + 0, /* minimum */ + 15, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range u8_range = { + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + +/* range for int value in [0-15] */ +static const SANE_Range value16_range = { + 0, /* minimum */ + 15, /* maximum */ + 1 /* quantization */ +}; + +/* range for buffer size */ +static const SANE_Range buffer_range = { + 2048, /* minimum */ + 4096 * 4096, /* maximum */ + 1 /* quantization */ +}; + +/* list of astra models */ +static const SANE_String_Const astra_models[] = + { "610", "1220", "1600", "2000", NULL }; + + +#define UMAX_PP_CHANNEL_RED 0 +#define UMAX_PP_CHANNEL_GREEN 1 +#define UMAX_PP_CHANNEL_BLUE 2 +#define UMAX_PP_CHANNEL_GRAY 1 + +#define UMAX_PP_STATE_SCANNING 2 +#define UMAX_PP_STATE_CANCELLED 1 +#define UMAX_PP_STATE_IDLE 0 + +#define UMAX_PP_MODE_LINEART 0 +#define UMAX_PP_MODE_GRAYSCALE 1 +#define UMAX_PP_MODE_COLOR 2 + +#define MM_TO_PIXEL(mm, res) (SANE_UNFIX(mm) * (float )res / MM_PER_INCH) +#define PIXEL_TO_MM(px, res) (SANE_FIX((float )(px * MM_PER_INCH / (res / 10)) / 10.0)) + +#define UMAX_PP_DEFAULT_PORT "/dev/parport0" + +#define UMAX_PP_RESERVE 259200 + +/* + * devname may be either an hardware address for direct I/O (0x378 for instance) + * or the device name used by ppdev on linux systems (/dev/parport0 ) + */ + + +static SANE_Status +umax_pp_attach (SANEI_Config * config, const char *devname) +{ + Umax_PP_Descriptor *dev; + int i; + SANE_Status status = SANE_STATUS_GOOD; + int ret, prt = 0, mdl; + char model[32]; + char name[64]; + char *val; + + memset (name, 0, 64); + + if ((strlen (devname) < 3)) + return SANE_STATUS_INVAL; + + sanei_umax_pp_setastra (atoi((SANE_Char *) config->values[CFG_ASTRA])); + + /* if the name begins with a slash, it's a device, else it's an addr */ + if (devname != NULL) + { + if ((devname[0] == '/')) + { + strncpy (name, devname, 64); + } + else + { + if ((devname[0] == '0') + && ((devname[1] == 'x') || (devname[1] == 'X'))) + prt = strtol (devname + 2, NULL, 16); + else + prt = atoi (devname); + } + } + + + for (i = 0; i < num_devices; i++) + { + if (devname[0] == '/') + { + if (strcmp (devlist[i].ppdevice, devname) == 0) + return SANE_STATUS_GOOD; + } + else + { + if (strcmp (devlist[i].port, devname) == 0) + return SANE_STATUS_GOOD; + } + } + + ret = sanei_umax_pp_attach (prt, name); + switch (ret) + { + case UMAX1220P_OK: + status = SANE_STATUS_GOOD; + break; + case UMAX1220P_BUSY: + status = SANE_STATUS_DEVICE_BUSY; + break; + case UMAX1220P_TRANSPORT_FAILED: + DBG (1, "umax_pp_attach: failed to init transport layer on %s\n", + devname); + status = SANE_STATUS_IO_ERROR; + break; + case UMAX1220P_PROBE_FAILED: + DBG (1, "umax_pp_attach: failed to probe scanner on %s\n", devname); + status = SANE_STATUS_IO_ERROR; + break; + } + + if (status != SANE_STATUS_GOOD) + { + DBG (2, "umax_pp_attach: couldn't attach to `%s' (%s)\n", devname, + sane_strstatus (status)); + DEBUG (); + return status; + } + + + /* now look for the model */ + do + { + ret = sanei_umax_pp_model (prt, &mdl); + if (ret != UMAX1220P_OK) + { + DBG (1, "umax_pp_attach: waiting for busy scanner on %s\n", + devname); + } + } + while (ret == UMAX1220P_BUSY); + + if (ret != UMAX1220P_OK) + { + DBG (1, "umax_pp_attach: failed to recognize scanner model on %s\n", + devname); + return SANE_STATUS_IO_ERROR; + } + sprintf (model, "Astra %dP", mdl); + + + dev = malloc (sizeof (Umax_PP_Descriptor) * (num_devices + 1)); + + if (dev == NULL) + { + DBG (2, "umax_pp_attach: not enough memory for device descriptor\n"); + DEBUG (); + return SANE_STATUS_NO_MEM; + } + + memset (dev, 0, sizeof (Umax_PP_Descriptor) * (num_devices + 1)); + + if (num_devices > 0) + { + memcpy (dev + 1, devlist, sizeof (Umax_PP_Descriptor) * (num_devices)); + free (devlist); + } + + devlist = dev; + num_devices++; + + /* if there are user provided values, use them */ + val=(SANE_Char *) config->values[CFG_NAME]; + if(strlen(val)==0) + dev->sane.name = strdup (devname); + else + dev->sane.name = strdup (val); + val=(SANE_Char *) config->values[CFG_VENDOR]; + if(strlen(val)==0) + dev->sane.vendor = strdup ("UMAX"); + else + dev->sane.vendor = strdup (val); + dev->sane.type = "flatbed scanner"; + + if (devname[0] == '/') + dev->ppdevice = strdup (devname); + else + dev->port = strdup (devname); + dev->buf_size = buf_size; + + if (mdl > 610) + { /* Astra 1220, 1600 and 2000 */ + dev->max_res = 1200; + dev->ccd_res = 600; + dev->max_h_size = 5100; + dev->max_v_size = 7000 - 8; /* -8: workaround 'y overflow bug at 600 dpi' */ + } + else + { /* Astra 610 */ + dev->max_res = 600; + dev->ccd_res = 300; + dev->max_h_size = 2550; + dev->max_v_size = 3500; + } + val=(SANE_Char *) config->values[CFG_MODEL]; + if(strlen(val)==0) + dev->sane.model = strdup (model); + else + dev->sane.model = strdup (val); + + + DBG (3, "umax_pp_attach: device %s attached\n", devname); + + return SANE_STATUS_GOOD; +} + +/* + * walk a port list and try to attach to them + * + */ +static SANE_Int +umax_pp_try_ports (SANEI_Config * config, char **ports) +{ + int i; + int rc = SANE_STATUS_INVAL; + + if (ports != NULL) + { + i = 0; + rc = SANE_STATUS_INVAL; + while (ports[i] != NULL) + { + if (rc != SANE_STATUS_GOOD) + { + DBG (3, "umax_pp_try_ports: trying port `%s'\n", ports[i]); + rc = umax_pp_attach (config, ports[i]); + if (rc != SANE_STATUS_GOOD) + DBG (3, "umax_pp_try_ports: couldn't attach to port `%s'\n", + ports[i]); + else + DBG (3, + "umax_pp_try_ports: attach to port `%s' successfull\n", + ports[i]); + } + free (ports[i]); + i++; + } + free (ports); + } + return rc; +} + +/* + * attempt to auto detect right parallel port + * if safe set to SANE_TRUE, no direct hardware access + * is tried + */ +static SANE_Int +umax_pp_auto_attach (SANEI_Config * config, SANE_Int safe) +{ + char **ports; + int rc = SANE_STATUS_INVAL; + + /* safe tests: user parallel port devices */ + ports = sanei_parport_find_device (); + if (ports != NULL) + rc = umax_pp_try_ports (config, ports); + + /* try for direct hardware access */ + if ((safe != SANE_TRUE) && (rc != SANE_STATUS_GOOD)) + { + ports = sanei_parport_find_port (); + if (ports != NULL) + rc = umax_pp_try_ports (config, ports); + } + return rc; +} + +/** callback use by sanei_configure_attach, it is called with the + * device name to use for attach try. + */ +static SANE_Status +umax_pp_configure_attach (SANEI_Config * config, const char *devname) +{ + const char *lp; + SANE_Char *token; + SANE_Status status = SANE_STATUS_INVAL; + + /* check for mandatory 'port' token */ + lp = sanei_config_get_string (devname, &token); + if (strncmp (token, "port", 4) != 0) + { + DBG (3, "umax_pp_configure_attach: invalid port line `%s'\n", devname); + free (token); + return SANE_STATUS_INVAL; + } + free (token); + + /* get argument */ + lp = sanei_config_get_string (lp, &token); + + /* if "safe-auto" or "auto" devname, use umax_pp_attach_auto */ + if (strncmp (token, "safe-auto", 9) == 0) + { + status = umax_pp_auto_attach (config, SANE_TRUE); + } + else if (strncmp (token, "auto", 4) == 0) + { + status = umax_pp_auto_attach (config, SANE_FALSE); + } + else + { + status = umax_pp_attach (config, token); + } + free (token); + return status; +} + +static SANE_Int +umax_pp_get_sync (SANE_Int dpi) +{ + /* delta between color frames */ + if (sanei_umax_pp_getastra () > 610) + { + switch (dpi) + { + case 1200: + return 8; + case 600: + return 4; + case 300: + return 2; + case 150: + return 1; + default: + return 0; + } + } + else + { + switch (dpi) + { + case 600: + return 16; + case 300: + return 8; /* 8 double-checked */ + case 150: + /* wrong: 2, 3, 5 + * double-checked : 4 + */ + return 4; + default: + return 2; /* 2 double-checked */ + } + } +} + + +static SANE_Status +init_options (Umax_PP_Device * dev) +{ + int i; + + /* sets initial option value to zero */ + memset (dev->opt, 0, sizeof (dev->opt)); + memset (dev->val, 0, sizeof (dev->val)); + + for (i = 0; i < NUM_OPTIONS; ++i) + { + dev->opt[i].size = sizeof (SANE_Word); + dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + dev->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS; + + /* "Mode" group: */ + + dev->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE; + dev->opt[OPT_MODE_GROUP].name = ""; + dev->opt[OPT_MODE_GROUP].desc = ""; + dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_MODE_GROUP].size = 0; + dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* scan mode */ + dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + dev->opt[OPT_MODE].type = SANE_TYPE_STRING; + dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + dev->opt[OPT_MODE].size = 10; + dev->opt[OPT_MODE].constraint.string_list = mode_list; + dev->val[OPT_MODE].s = strdup (mode_list[1]); + + /* resolution */ + dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED; + dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_RESOLUTION].constraint.range = &dev->dpi_range; + dev->val[OPT_RESOLUTION].w = dev->dpi_range.min; + + + /* preview */ + dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + dev->opt[OPT_PREVIEW].size = sizeof (SANE_Word); + dev->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE; + dev->val[OPT_PREVIEW].w = SANE_FALSE; + + /* gray preview */ + dev->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; + dev->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; + dev->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; + dev->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; + dev->opt[OPT_GRAY_PREVIEW].size = sizeof (SANE_Word); + dev->opt[OPT_GRAY_PREVIEW].unit = SANE_UNIT_NONE; + dev->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; + + /* "Geometry" group: */ + + dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); + dev->opt[OPT_GEOMETRY_GROUP].desc = ""; + dev->opt[OPT_GEOMETRY_GROUP].name = ""; + dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_GEOMETRY_GROUP].size = 0; + dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* top-left x */ + dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + dev->opt[OPT_TL_X].type = SANE_TYPE_INT; + dev->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL; + dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_X].constraint.range = &dev->x_range; + dev->val[OPT_TL_X].w = 0; + + /* top-left y */ + dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + dev->opt[OPT_TL_Y].type = SANE_TYPE_INT; + dev->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL; + dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_Y].constraint.range = &dev->y_range; + dev->val[OPT_TL_Y].w = 0; + + /* bottom-right x */ + dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + dev->opt[OPT_BR_X].type = SANE_TYPE_INT; + dev->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL; + dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_X].constraint.range = &dev->x_range; + dev->val[OPT_BR_X].w = dev->x_range.max; + + /* bottom-right y */ + dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + dev->opt[OPT_BR_Y].type = SANE_TYPE_INT; + dev->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL; + dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_Y].constraint.range = &dev->y_range; + dev->val[OPT_BR_Y].w = dev->y_range.max; + + /* "Enhancement" group: */ + + dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); + dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; + dev->opt[OPT_ENHANCEMENT_GROUP].name = ""; + dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_ENHANCEMENT_GROUP].size = 0; + dev->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_ADVANCED; + dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* lamp control */ + dev->opt[OPT_LAMP_CONTROL].name = "lamp-control"; + dev->opt[OPT_LAMP_CONTROL].title = SANE_I18N ("Lamp on"); + dev->opt[OPT_LAMP_CONTROL].desc = SANE_I18N ("Sets lamp on/off"); + dev->opt[OPT_LAMP_CONTROL].type = SANE_TYPE_BOOL; + dev->opt[OPT_LAMP_CONTROL].size = sizeof (SANE_Word); + dev->opt[OPT_LAMP_CONTROL].unit = SANE_UNIT_NONE; + dev->val[OPT_LAMP_CONTROL].w = SANE_TRUE; + dev->opt[OPT_LAMP_CONTROL].cap |= SANE_CAP_ADVANCED; + + /* UTA control */ + dev->opt[OPT_UTA_CONTROL].name = "UTA-control"; + dev->opt[OPT_UTA_CONTROL].title = SANE_I18N ("UTA on"); + dev->opt[OPT_UTA_CONTROL].desc = SANE_I18N ("Sets UTA on/off"); + dev->opt[OPT_UTA_CONTROL].type = SANE_TYPE_BOOL; + dev->opt[OPT_UTA_CONTROL].size = sizeof (SANE_Word); + dev->opt[OPT_UTA_CONTROL].unit = SANE_UNIT_NONE; + dev->val[OPT_UTA_CONTROL].w = SANE_TRUE; + dev->opt[OPT_UTA_CONTROL].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE; + + /* custom-gamma table */ + dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED; + dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + /* grayscale gamma vector */ + dev->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR].wa = &dev->gamma_table[0][0]; + + /* red gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR_R].wa = &dev->gamma_table[1][0]; + + /* green gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR_G].wa = &dev->gamma_table[2][0]; + + /* blue gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR_B].wa = &dev->gamma_table[3][0]; + + /* gain group */ + dev->opt[OPT_MANUAL_GAIN].name = "manual-channel-gain"; + dev->opt[OPT_MANUAL_GAIN].title = SANE_I18N ("Gain"); + dev->opt[OPT_MANUAL_GAIN].desc = SANE_I18N ("Color channels gain settings"); + dev->opt[OPT_MANUAL_GAIN].type = SANE_TYPE_BOOL; + dev->opt[OPT_MANUAL_GAIN].cap |= SANE_CAP_ADVANCED; + dev->val[OPT_MANUAL_GAIN].w = SANE_FALSE; + + /* gray gain */ + dev->opt[OPT_GRAY_GAIN].name = "gray-gain"; + dev->opt[OPT_GRAY_GAIN].title = SANE_I18N ("Gray gain"); + dev->opt[OPT_GRAY_GAIN].desc = SANE_I18N ("Sets gray channel gain"); + dev->opt[OPT_GRAY_GAIN].type = SANE_TYPE_INT; + dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GRAY_GAIN].unit = SANE_UNIT_NONE; + dev->opt[OPT_GRAY_GAIN].size = sizeof (SANE_Int); + dev->opt[OPT_GRAY_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GRAY_GAIN].constraint.range = &u4_range; + dev->val[OPT_GRAY_GAIN].w = dev->gray_gain; + + /* red gain */ + dev->opt[OPT_RED_GAIN].name = "red-gain"; + dev->opt[OPT_RED_GAIN].title = SANE_I18N ("Red gain"); + dev->opt[OPT_RED_GAIN].desc = SANE_I18N ("Sets red channel gain"); + dev->opt[OPT_RED_GAIN].type = SANE_TYPE_INT; + dev->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_RED_GAIN].unit = SANE_UNIT_NONE; + dev->opt[OPT_RED_GAIN].size = sizeof (SANE_Int); + dev->opt[OPT_RED_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_RED_GAIN].constraint.range = &u4_range; + dev->val[OPT_RED_GAIN].w = dev->red_gain; + + /* green gain */ + dev->opt[OPT_GREEN_GAIN].name = "green-gain"; + dev->opt[OPT_GREEN_GAIN].title = SANE_I18N ("Green gain"); + dev->opt[OPT_GREEN_GAIN].desc = SANE_I18N ("Sets green channel gain"); + dev->opt[OPT_GREEN_GAIN].type = SANE_TYPE_INT; + dev->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GREEN_GAIN].unit = SANE_UNIT_NONE; + dev->opt[OPT_GREEN_GAIN].size = sizeof (SANE_Int); + dev->opt[OPT_GREEN_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GREEN_GAIN].constraint.range = &u4_range; + dev->val[OPT_GREEN_GAIN].w = dev->green_gain; + + /* blue gain */ + dev->opt[OPT_BLUE_GAIN].name = "blue-gain"; + dev->opt[OPT_BLUE_GAIN].title = SANE_I18N ("Blue gain"); + dev->opt[OPT_BLUE_GAIN].desc = SANE_I18N ("Sets blue channel gain"); + dev->opt[OPT_BLUE_GAIN].type = SANE_TYPE_INT; + dev->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_BLUE_GAIN].unit = SANE_UNIT_NONE; + dev->opt[OPT_BLUE_GAIN].size = sizeof (SANE_Int); + dev->opt[OPT_BLUE_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BLUE_GAIN].constraint.range = &u4_range; + dev->val[OPT_BLUE_GAIN].w = dev->blue_gain; + + /* offset group */ + dev->opt[OPT_MANUAL_OFFSET].name = "manual-offset"; + dev->opt[OPT_MANUAL_OFFSET].title = SANE_I18N ("Offset"); + dev->opt[OPT_MANUAL_OFFSET].desc = + SANE_I18N ("Color channels offset settings"); + dev->opt[OPT_MANUAL_OFFSET].type = SANE_TYPE_BOOL; + dev->opt[OPT_MANUAL_OFFSET].cap |= SANE_CAP_ADVANCED; + dev->val[OPT_MANUAL_OFFSET].w = SANE_FALSE; + + /* gray offset */ + dev->opt[OPT_GRAY_OFFSET].name = "gray-offset"; + dev->opt[OPT_GRAY_OFFSET].title = SANE_I18N ("Gray offset"); + dev->opt[OPT_GRAY_OFFSET].desc = SANE_I18N ("Sets gray channel offset"); + dev->opt[OPT_GRAY_OFFSET].type = SANE_TYPE_INT; + dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GRAY_OFFSET].unit = SANE_UNIT_NONE; + dev->opt[OPT_GRAY_OFFSET].size = sizeof (SANE_Int); + dev->opt[OPT_GRAY_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GRAY_OFFSET].constraint.range = &u4_range; + dev->val[OPT_GRAY_OFFSET].w = dev->gray_offset; + + /* red offset */ + dev->opt[OPT_RED_OFFSET].name = "red-offset"; + dev->opt[OPT_RED_OFFSET].title = SANE_I18N ("Red offset"); + dev->opt[OPT_RED_OFFSET].desc = SANE_I18N ("Sets red channel offset"); + dev->opt[OPT_RED_OFFSET].type = SANE_TYPE_INT; + dev->opt[OPT_RED_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_RED_OFFSET].unit = SANE_UNIT_NONE; + dev->opt[OPT_RED_OFFSET].size = sizeof (SANE_Int); + dev->opt[OPT_RED_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_RED_OFFSET].constraint.range = &u4_range; + dev->val[OPT_RED_OFFSET].w = dev->red_offset; + + /* green offset */ + dev->opt[OPT_GREEN_OFFSET].name = "green-offset"; + dev->opt[OPT_GREEN_OFFSET].title = SANE_I18N ("Green offset"); + dev->opt[OPT_GREEN_OFFSET].desc = SANE_I18N ("Sets green channel offset"); + dev->opt[OPT_GREEN_OFFSET].type = SANE_TYPE_INT; + dev->opt[OPT_GREEN_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GREEN_OFFSET].unit = SANE_UNIT_NONE; + dev->opt[OPT_GREEN_OFFSET].size = sizeof (SANE_Int); + dev->opt[OPT_GREEN_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GREEN_OFFSET].constraint.range = &u4_range; + dev->val[OPT_GREEN_OFFSET].w = dev->green_offset; + + /* blue offset */ + dev->opt[OPT_BLUE_OFFSET].name = "blue-offset"; + dev->opt[OPT_BLUE_OFFSET].title = SANE_I18N ("Blue offset"); + dev->opt[OPT_BLUE_OFFSET].desc = SANE_I18N ("Sets blue channel offset"); + dev->opt[OPT_BLUE_OFFSET].type = SANE_TYPE_INT; + dev->opt[OPT_BLUE_OFFSET].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_BLUE_OFFSET].unit = SANE_UNIT_NONE; + dev->opt[OPT_BLUE_OFFSET].size = sizeof (SANE_Int); + dev->opt[OPT_BLUE_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BLUE_OFFSET].constraint.range = &u4_range; + dev->val[OPT_BLUE_OFFSET].w = dev->blue_offset; + + return SANE_STATUS_GOOD; +} + + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + SANE_Status status; + SANEI_Config config; + SANE_Option_Descriptor *options[NUM_CFG_OPTIONS]; + void *values[NUM_CFG_OPTIONS]; + int i = 0; + + DBG_INIT (); + + if (authorize != NULL) + { + DBG (2, "init: SANE_Auth_Callback not supported ...\n"); + } + + if (version_code != NULL) + *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, UMAX_PP_BUILD); + + DBG (3, "init: SANE v%s, backend v%d.%d.%d-%s\n", VERSION, SANE_CURRENT_MAJOR, V_MINOR, + UMAX_PP_BUILD, UMAX_PP_STATE); + + /* set up configuration options to parse */ + options[CFG_BUFFER] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_BUFFER]->name = "buffer"; + options[CFG_BUFFER]->type = SANE_TYPE_INT; + options[CFG_BUFFER]->unit = SANE_UNIT_NONE; + options[CFG_BUFFER]->size = sizeof (SANE_Word); + options[CFG_BUFFER]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_BUFFER]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_BUFFER]->constraint.range = &buffer_range; + values[CFG_BUFFER] = &buf_size; + + options[CFG_RED_GAIN] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_RED_GAIN]->name = "red-gain"; + options[CFG_RED_GAIN]->type = SANE_TYPE_INT; + options[CFG_RED_GAIN]->unit = SANE_UNIT_NONE; + options[CFG_RED_GAIN]->size = sizeof (SANE_Word); + options[CFG_RED_GAIN]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_RED_GAIN]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_RED_GAIN]->constraint.range = &value16_range; + values[CFG_RED_GAIN] = &red_gain; + + options[CFG_GREEN_GAIN] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_GREEN_GAIN]->name = "green-gain"; + options[CFG_GREEN_GAIN]->type = SANE_TYPE_INT; + options[CFG_GREEN_GAIN]->unit = SANE_UNIT_NONE; + options[CFG_GREEN_GAIN]->size = sizeof (SANE_Word); + options[CFG_GREEN_GAIN]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_GREEN_GAIN]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_GREEN_GAIN]->constraint.range = &value16_range; + values[CFG_GREEN_GAIN] = &green_gain; + + options[CFG_BLUE_GAIN] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_BLUE_GAIN]->name = "blue-gain"; + options[CFG_BLUE_GAIN]->type = SANE_TYPE_INT; + options[CFG_BLUE_GAIN]->unit = SANE_UNIT_NONE; + options[CFG_BLUE_GAIN]->size = sizeof (SANE_Word); + options[CFG_BLUE_GAIN]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_BLUE_GAIN]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_BLUE_GAIN]->constraint.range = &value16_range; + values[CFG_BLUE_GAIN] = &blue_gain; + + options[CFG_RED_OFFSET] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_RED_OFFSET]->name = "red-offset"; + options[CFG_RED_OFFSET]->type = SANE_TYPE_INT; + options[CFG_RED_OFFSET]->unit = SANE_UNIT_NONE; + options[CFG_RED_OFFSET]->size = sizeof (SANE_Word); + options[CFG_RED_OFFSET]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_RED_OFFSET]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_RED_OFFSET]->constraint.range = &value16_range; + values[CFG_RED_OFFSET] = &red_offset; + + options[CFG_GREEN_OFFSET] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_GREEN_OFFSET]->name = "green-offset"; + options[CFG_GREEN_OFFSET]->type = SANE_TYPE_INT; + options[CFG_GREEN_OFFSET]->unit = SANE_UNIT_NONE; + options[CFG_GREEN_OFFSET]->size = sizeof (SANE_Word); + options[CFG_GREEN_OFFSET]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_GREEN_OFFSET]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_GREEN_OFFSET]->constraint.range = &value16_range; + values[CFG_GREEN_OFFSET] = &green_offset; + + options[CFG_BLUE_OFFSET] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_BLUE_OFFSET]->name = "blue-offset"; + options[CFG_BLUE_OFFSET]->type = SANE_TYPE_INT; + options[CFG_BLUE_OFFSET]->unit = SANE_UNIT_NONE; + options[CFG_BLUE_OFFSET]->size = sizeof (SANE_Word); + options[CFG_BLUE_OFFSET]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_BLUE_OFFSET]->constraint_type = SANE_CONSTRAINT_RANGE; + options[CFG_BLUE_OFFSET]->constraint.range = &value16_range; + values[CFG_BLUE_OFFSET] = &blue_offset; + + options[CFG_VENDOR] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_VENDOR]->name = "vendor"; + options[CFG_VENDOR]->type = SANE_TYPE_STRING; + options[CFG_VENDOR]->unit = SANE_UNIT_NONE; + options[CFG_VENDOR]->size = 128; + options[CFG_VENDOR]->cap = SANE_CAP_SOFT_SELECT; + values[CFG_VENDOR] = scanner_vendor; + + options[CFG_NAME] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_NAME]->name = "name"; + options[CFG_NAME]->type = SANE_TYPE_STRING; + options[CFG_NAME]->unit = SANE_UNIT_NONE; + options[CFG_NAME]->size = 128; + options[CFG_NAME]->cap = SANE_CAP_SOFT_SELECT; + values[CFG_NAME] = scanner_name; + + options[CFG_MODEL] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_MODEL]->name = "model"; + options[CFG_MODEL]->type = SANE_TYPE_STRING; + options[CFG_MODEL]->unit = SANE_UNIT_NONE; + options[CFG_MODEL]->size = 128; + options[CFG_MODEL]->cap = SANE_CAP_SOFT_SELECT; + values[CFG_MODEL] = scanner_model; + + options[CFG_ASTRA] = + (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); + options[CFG_ASTRA]->name = "astra"; + options[CFG_ASTRA]->type = SANE_TYPE_STRING; + options[CFG_ASTRA]->unit = SANE_UNIT_NONE; + options[CFG_ASTRA]->size = 128; + options[CFG_ASTRA]->cap = SANE_CAP_SOFT_SELECT; + options[CFG_ASTRA]->constraint_type = SANE_CONSTRAINT_STRING_LIST; + options[CFG_ASTRA]->constraint.string_list = astra_models; + values[CFG_ASTRA] = astra; + + config.descriptors = options; + config.values = values; + config.count = NUM_CFG_OPTIONS; + + /* generic configure and attach function */ + status = sanei_configure_attach (UMAX_PP_CONFIG_FILE, &config, + umax_pp_configure_attach); + + /* free option descriptors */ + for (i = 0; i < NUM_CFG_OPTIONS; i++) + { + free (options[i]); + } + + return status; +} + +void +sane_exit (void) +{ + int i; + Umax_PP_Device *dev; + + DBG (3, "sane_exit: (...)\n"); + if (first_dev) + DBG (3, "exit: closing open devices\n"); + + while (first_dev) + { + dev = first_dev; + sane_close (dev); + } + + for (i = 0; i < num_devices; i++) + { + free (devlist[i].port); + free (devlist[i].sane.name); + free (devlist[i].sane.model); + free (devlist[i].sane.vendor); + } + + if (devlist != NULL) + { + free (devlist); + devlist = NULL; + } + + if (devarray != NULL) + { + free (devarray); + devarray = NULL; + } + + /* reset values */ + num_devices = 0; + first_dev = NULL; + + red_gain = 0; + green_gain = 0; + blue_gain = 0; + + red_offset = 0; + green_offset = 0; + blue_offset = 0; + +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + int i; + + DBG (3, "get_devices\n"); + DBG (129, "unused arg: local_only = %d\n", (int) local_only); + + if (devarray != NULL) + { + free (devarray); + devarray = NULL; + } + + devarray = malloc ((num_devices + 1) * sizeof (devarray[0])); + + if (devarray == NULL) + { + DBG (2, "get_devices: not enough memory for device list\n"); + DEBUG (); + return SANE_STATUS_NO_MEM; + } + + for (i = 0; i < num_devices; i++) + devarray[i] = &devlist[i].sane; + + devarray[num_devices] = NULL; + *device_list = devarray; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Umax_PP_Device *dev; + Umax_PP_Descriptor *desc; + int i, j; + int rc, prt = 0; + char *name = NULL; + + DBG (3, "open: device `%s'\n", devicename); + + /* if no device given or 'umax_pp' default value given */ + if (devicename == NULL || devicename[0] == 0 + || strncmp (devicename, "umax_pp", 7) == 0) + { + + if (num_devices == 0) + { + DBG (1, "open: no devices present\n"); + return SANE_STATUS_INVAL; + } + + DBG (3, "open: trying default device %s, port=%s,ppdev=%s\n", + devlist[0].sane.name, devlist[0].port, devlist[0].ppdevice); + if (devlist[0].port != NULL) + { + if ((devlist[0].port[0] == '0') + && ((devlist[0].port[1] == 'x') || (devlist[0].port[1] == 'X'))) + prt = strtol (devlist[0].port + 2, NULL, 16); + else + prt = atoi (devlist[0].port); + rc = sanei_umax_pp_open (prt, NULL); + } + else + { + rc = sanei_umax_pp_open (0, devlist[0].ppdevice); + } + desc = &devlist[0]; + } + else /* specific value */ + { + for (i = 0; i < num_devices; i++) + if (strcmp (devlist[i].sane.name, devicename) == 0) + break; + + if (i >= num_devices) + for (i = 0; i < num_devices; i++) + if (strcmp (devlist[i].port, devicename) == 0) + break; + + if (i >= num_devices) + { + DBG (2, "open: device doesn't exist\n"); + DEBUG (); + return SANE_STATUS_INVAL; + } + + desc = &devlist[i]; + + if (devlist[i].ppdevice != NULL) + { + if (devlist[i].ppdevice[0] == '/') + { + name = devlist[i].ppdevice; + } + } + else + { + if ((devlist[i].port[0] == '0') + && ((devlist[i].port[1] == 'x') || (devlist[i].port[1] == 'X'))) + prt = strtol (devlist[i].port + 2, NULL, 16); + else + prt = atoi (devlist[i].port); + DBG (64, "open: devlist[i].port='%s' -> port=0x%X\n", + devlist[i].port, prt); + } + rc = sanei_umax_pp_open (prt, name); + } + + /* treat return code from open */ + switch (rc) + { + case UMAX1220P_TRANSPORT_FAILED: + if (name == NULL) + { + DBG (1, "failed to init transport layer on port 0x%03X\n", prt); + } + else + { + DBG (1, "failed to init transport layer on device %s\n", name); + } + return SANE_STATUS_IO_ERROR; + + case UMAX1220P_SCANNER_FAILED: + if (name == NULL) + { + DBG (1, "failed to initialize scanner on port 0x%03X\n", prt); + } + else + { + DBG (1, "failed to initialize scanner on device %s\n", name); + } + return SANE_STATUS_IO_ERROR; + case UMAX1220P_BUSY: + if (name == NULL) + { + DBG (1, "busy scanner on port 0x%03X\n", prt); + } + else + { + DBG (1, "busy scanner on device %s\n", name); + } + return SANE_STATUS_DEVICE_BUSY; + } + + + dev = (Umax_PP_Device *) malloc (sizeof (*dev)); + + if (dev == NULL) + { + DBG (2, "open: not enough memory for device descriptor\n"); + DEBUG (); + return SANE_STATUS_NO_MEM; + } + + memset (dev, 0, sizeof (*dev)); + + dev->desc = desc; + + for (i = 0; i < 4; ++i) + for (j = 0; j < 256; ++j) + dev->gamma_table[i][j] = j; + + /* the extra amount of UMAX_PP_RESERVE bytes is to handle */ + /* the data needed to resync the color frames */ + dev->buf = malloc (dev->desc->buf_size + UMAX_PP_RESERVE); + dev->bufsize = dev->desc->buf_size; + + dev->dpi_range.min = SANE_FIX (75); + dev->dpi_range.max = SANE_FIX (dev->desc->max_res); + dev->dpi_range.quant = 0; + + dev->x_range.min = 0; + dev->x_range.max = dev->desc->max_h_size; + dev->x_range.quant = 0; + + dev->y_range.min = 0; + dev->y_range.max = dev->desc->max_v_size; + dev->y_range.quant = 0; + + dev->gray_gain = 0; + + /* use pre defined settings read from umax_pp.conf */ + dev->red_gain = red_gain; + dev->green_gain = green_gain; + dev->blue_gain = blue_gain; + dev->red_offset = red_offset; + dev->green_offset = green_offset; + dev->blue_offset = blue_offset; + + + if (dev->buf == NULL) + { + DBG (2, "open: not enough memory for scan buffer (%lu bytes)\n", + (long int) dev->desc->buf_size); + DEBUG (); + free (dev); + return SANE_STATUS_NO_MEM; + } + + init_options (dev); + + dev->next = first_dev; + first_dev = dev; + + + if (sanei_umax_pp_UTA () == 1) + dev->opt[OPT_UTA_CONTROL].cap &= ~SANE_CAP_INACTIVE; + + *handle = dev; + + DBG (3, "open: success\n"); + + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + Umax_PP_Device *prev, *dev; + int rc; + + DBG (3, "sane_close: ...\n"); + /* remove handle from list of open handles: */ + prev = NULL; + + for (dev = first_dev; dev; dev = dev->next) + { + if (dev == handle) + break; + prev = dev; + } + + if (dev == NULL) + { + DBG (2, "close: unknown device\n"); + DEBUG (); + return; /* oops, not a handle we know about */ + } + + if (dev->state == UMAX_PP_STATE_SCANNING) + sane_cancel (handle); /* remember: sane_cancel is a macro and + expands to sane_umax_pp_cancel ()... */ + + + /* if the scanner is parking head, we wait it to finish */ + while (dev->state == UMAX_PP_STATE_CANCELLED) + { + DBG (2, "close: waiting scanner to park head\n"); + rc = sanei_umax_pp_status (); + + /* check if scanner busy parking */ + if (rc != UMAX1220P_BUSY) + { + DBG (2, "close: scanner head parked\n"); + dev->state = UMAX_PP_STATE_IDLE; + } + } + + /* then we switch off gain if needed */ + if (dev->val[OPT_LAMP_CONTROL].w == SANE_TRUE) + { + rc = sanei_umax_pp_lamp (0); + if (rc == UMAX1220P_TRANSPORT_FAILED) + { + DBG (1, "close: switch off gain failed (ignored....)\n"); + } + } + + sanei_umax_pp_close (); + + + + if (prev != NULL) + prev->next = dev->next; + else + first_dev = dev->next; + + free (dev->buf); + DBG (3, "close: device closed\n"); + + free (handle); + +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Umax_PP_Device *dev = handle; + + if ((unsigned) option >= NUM_OPTIONS) + { + DBG (2, "get_option_descriptor: option %d doesn't exist\n", option); + DEBUG (); + return NULL; + } + + DBG (6, "get_option_descriptor: requested option %d (%s)\n", + option, dev->opt[option].name); + + return dev->opt + option; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + Umax_PP_Device *dev = handle; + SANE_Status status; + SANE_Word w, cap, tmpw; + int dpi, rc; + + DBG (6, "control_option: option %d, action %d\n", option, action); + + if (info) + *info = 0; + + if (dev->state == UMAX_PP_STATE_SCANNING) + { + DBG (2, "control_option: device is scanning\n"); + return SANE_STATUS_DEVICE_BUSY; + } + + if ((unsigned int) option >= NUM_OPTIONS) + { + DBG (2, "control_option: option doesn't exist\n"); + return SANE_STATUS_INVAL; + } + + + cap = dev->opt[option].cap; + + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + DBG (2, "control_option: option isn't active\n"); + return SANE_STATUS_INVAL; + } + + DBG (6, "control_option: option <%s>, action ... %d\n", + dev->opt[option].name, action); + + if (action == SANE_ACTION_GET_VALUE) + { + DBG (6, " get value\n"); + switch (option) + { + /* word options: */ + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + case OPT_LAMP_CONTROL: + case OPT_UTA_CONTROL: + case OPT_RESOLUTION: + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_NUM_OPTS: + case OPT_CUSTOM_GAMMA: + case OPT_MANUAL_GAIN: + case OPT_GRAY_GAIN: + case OPT_GREEN_GAIN: + case OPT_RED_GAIN: + case OPT_BLUE_GAIN: + case OPT_MANUAL_OFFSET: + case OPT_GRAY_OFFSET: + case OPT_GREEN_OFFSET: + case OPT_RED_OFFSET: + case OPT_BLUE_OFFSET: + + *(SANE_Word *) val = dev->val[option].w; + return SANE_STATUS_GOOD; + + /* word-array options: */ + case OPT_GAMMA_VECTOR: + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + memcpy (val, dev->val[option].wa, dev->opt[option].size); + return SANE_STATUS_GOOD; + + /* string options: */ + case OPT_MODE: + + strcpy (val, dev->val[option].s); + return SANE_STATUS_GOOD; + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + DBG (6, " set value\n"); + + if (!SANE_OPTION_IS_SETTABLE (cap)) + { + DBG (2, "control_option: option can't be set\n"); + return SANE_STATUS_INVAL; + } + + status = sanei_constrain_value (dev->opt + option, val, info); + + if (status != SANE_STATUS_GOOD) + { + DBG (2, "control_option: constrain_value failed (%s)\n", + sane_strstatus (status)); + return status; + } + + if (option == OPT_RESOLUTION) + { + DBG (16, "control_option: setting resolution to %d\n", + *(SANE_Int *) val); + } + if (option == OPT_PREVIEW) + { + DBG (16, "control_option: setting preview to %d\n", + *(SANE_Word *) val); + } + + switch (option) + { + /* (mostly) side-effect-free word options: */ + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + case OPT_TL_Y: + case OPT_BR_Y: + + if (info) + *info |= SANE_INFO_RELOAD_PARAMS; + + case OPT_GRAY_GAIN: + case OPT_GREEN_GAIN: + case OPT_RED_GAIN: + case OPT_BLUE_GAIN: + case OPT_GRAY_OFFSET: + case OPT_GREEN_OFFSET: + case OPT_RED_OFFSET: + case OPT_BLUE_OFFSET: + + dev->val[option].w = *(SANE_Word *) val; + /* sanity check */ + if (dev->val[OPT_BR_Y].w < dev->val[OPT_TL_Y].w) + { + tmpw = dev->val[OPT_BR_Y].w; + dev->val[OPT_BR_Y].w = dev->val[OPT_TL_Y].w; + dev->val[OPT_TL_Y].w = tmpw; + if (info) + *info |= SANE_INFO_INEXACT; + DBG (16, "control_option: swapping Y coordinates\n"); + } + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w)); + if (dev->val[OPT_TL_Y].w < 2 * umax_pp_get_sync (dpi)) + { + DBG (16, "control_option: correcting TL_Y coordinates\n"); + dev->val[OPT_TL_Y].w = 2 * umax_pp_get_sync (dpi); + if (info) + *info |= SANE_INFO_INEXACT; + } + } + return SANE_STATUS_GOOD; + + /* side-effect-free word-array options: */ + case OPT_GAMMA_VECTOR: + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + + memcpy (dev->val[option].wa, val, dev->opt[option].size); + return SANE_STATUS_GOOD; + + + /* options with side-effects: */ + case OPT_UTA_CONTROL: + dev->val[option].w = *(SANE_Word *) val; + return SANE_STATUS_GOOD; + + case OPT_LAMP_CONTROL: + if (dev->state != UMAX_PP_STATE_IDLE) + { + rc = sanei_umax_pp_status (); + + /* check if scanner busy parking */ + if (rc == UMAX1220P_BUSY) + { + DBG (2, "control_option: scanner busy\n"); + if (info) + *info |= SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_DEVICE_BUSY; + } + dev->state = UMAX_PP_STATE_IDLE; + } + dev->val[option].w = *(SANE_Word *) val; + if (dev->val[option].w == SANE_TRUE) + rc = sanei_umax_pp_lamp (1); + else + rc = sanei_umax_pp_lamp (0); + if (rc == UMAX1220P_TRANSPORT_FAILED) + return SANE_STATUS_IO_ERROR; + return SANE_STATUS_GOOD; + + case OPT_TL_X: + case OPT_BR_X: + if (info) + *info |= SANE_INFO_RELOAD_PARAMS; + dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w)); + dev->val[option].w = *(SANE_Word *) val; + /* coords rounded to allow 32 bit IO/transfer */ + /* at high resolution */ + if (dpi >= 600) + { + if (dev->val[option].w & 0x03) + { + if (info) + *info |= SANE_INFO_INEXACT; + dev->val[option].w = dev->val[option].w & 0xFFFC; + *(SANE_Word *) val = dev->val[option].w; + DBG (16, "control_option: rounding X to %d\n", + *(SANE_Word *) val); + } + } + /* sanity check */ + if (dev->val[OPT_BR_X].w < dev->val[OPT_TL_X].w) + { + tmpw = dev->val[OPT_BR_X].w; + dev->val[OPT_BR_X].w = dev->val[OPT_TL_X].w; + dev->val[OPT_TL_X].w = tmpw; + if (info) + *info |= SANE_INFO_INEXACT; + DBG (16, "control_option: swapping X coordinates\n"); + } + return SANE_STATUS_GOOD; + + + + case OPT_RESOLUTION: + if (info) + *info |= SANE_INFO_RELOAD_PARAMS; + /* resolution : only have 75, 150, 300, 600 and 1200 */ + dpi = (int) (SANE_UNFIX (*(SANE_Word *) val)); + if ((dpi != 75) + && (dpi != 150) + && (dpi != 300) && (dpi != 600) && (dpi != 1200)) + { + if (dpi <= 75) + dpi = 75; + else if (dpi <= 150) + dpi = 150; + else if (dpi <= 300) + dpi = 300; + else if (dpi <= 600) + dpi = 600; + else + dpi = 1200; + if (info) + *info |= SANE_INFO_INEXACT; + *(SANE_Word *) val = SANE_FIX ((SANE_Word) dpi); + } + dev->val[option].w = *(SANE_Word *) val; + + /* correct top x and bottom x if needed */ + if (dpi >= 600) + { + dev->val[OPT_TL_X].w = dev->val[OPT_TL_X].w & 0xFFFC; + dev->val[OPT_BR_X].w = dev->val[OPT_BR_X].w & 0xFFFC; + } + /* corrects top y for offset */ + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + if (dev->val[OPT_TL_Y].w < 2 * umax_pp_get_sync (dpi)) + { + DBG (16, "control_option: correcting TL_Y coordinates\n"); + dev->val[OPT_TL_Y].w = 2 * umax_pp_get_sync (dpi); + if (info) + *info |= SANE_INFO_INEXACT; + } + } + return SANE_STATUS_GOOD; + + case OPT_MANUAL_OFFSET: + w = *(SANE_Word *) val; + + if (w == dev->val[OPT_MANUAL_OFFSET].w) + return SANE_STATUS_GOOD; /* no change */ + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS; + + dev->val[OPT_MANUAL_OFFSET].w = w; + + if (w == SANE_TRUE) + { + const char *mode = dev->val[OPT_MODE].s; + + if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)) + dev->opt[OPT_GRAY_OFFSET].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_RED_OFFSET].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_OFFSET].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_OFFSET].cap &= ~SANE_CAP_INACTIVE; + } + } + else + { + dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_RED_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_OFFSET].cap |= SANE_CAP_INACTIVE; + } + return SANE_STATUS_GOOD; + + + + case OPT_MANUAL_GAIN: + w = *(SANE_Word *) val; + + if (w == dev->val[OPT_MANUAL_GAIN].w) + return SANE_STATUS_GOOD; /* no change */ + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS; + + dev->val[OPT_MANUAL_GAIN].w = w; + + if (w == SANE_TRUE) + { + const char *mode = dev->val[OPT_MODE].s; + + if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)) + dev->opt[OPT_GRAY_GAIN].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_RED_GAIN].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_GAIN].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_GAIN].cap &= ~SANE_CAP_INACTIVE; + } + } + else + { + dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE; + } + return SANE_STATUS_GOOD; + + + + + case OPT_CUSTOM_GAMMA: + w = *(SANE_Word *) val; + + if (w == dev->val[OPT_CUSTOM_GAMMA].w) + return SANE_STATUS_GOOD; /* no change */ + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS; + + dev->val[OPT_CUSTOM_GAMMA].w = w; + + if (w == SANE_TRUE) + { + const char *mode = dev->val[OPT_MODE].s; + + if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)) + { + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + sanei_umax_pp_gamma (NULL, dev->val[OPT_GAMMA_VECTOR].wa, + NULL); + } + else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + sanei_umax_pp_gamma (dev->val[OPT_GAMMA_VECTOR_R].wa, + dev->val[OPT_GAMMA_VECTOR_G].wa, + dev->val[OPT_GAMMA_VECTOR_B].wa); + } + } + else + { + dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + sanei_umax_pp_gamma (NULL, NULL, NULL); + } + + return SANE_STATUS_GOOD; + + case OPT_MODE: + { + char *old_val = dev->val[option].s; + + if (old_val) + { + if (strcmp (old_val, val) == 0) + return SANE_STATUS_GOOD; /* no change */ + + free (old_val); + } + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + + dev->val[option].s = strdup (val); + + /* corrects top y for offset */ + if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w)); + if (dev->val[OPT_TL_Y].w < 2 * umax_pp_get_sync (dpi)) + { + dev->val[OPT_TL_Y].w = 2 * umax_pp_get_sync (dpi); + DBG (16, "control_option: correcting TL_Y coordinates\n"); + if (info) + *info |= SANE_INFO_INEXACT; + } + } + + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + sanei_umax_pp_gamma (NULL, NULL, NULL); + + + if (dev->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) + { + if ((strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) + || (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)) + { + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + sanei_umax_pp_gamma (NULL, dev->val[OPT_GAMMA_VECTOR].wa, + NULL); + } + else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + sanei_umax_pp_gamma (dev->val[OPT_GAMMA_VECTOR_R].wa, + dev->val[OPT_GAMMA_VECTOR_G].wa, + dev->val[OPT_GAMMA_VECTOR_B].wa); + } + } + + /* rebuild OPT OFFSET */ + dev->opt[OPT_GRAY_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_RED_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_OFFSET].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_OFFSET].cap |= SANE_CAP_INACTIVE; + + + if (dev->val[OPT_MANUAL_OFFSET].w == SANE_TRUE) + { + if ((strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) + || (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)) + dev->opt[OPT_GRAY_OFFSET].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->opt[OPT_RED_OFFSET].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_OFFSET].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_OFFSET].cap &= ~SANE_CAP_INACTIVE; + } + } + + /* rebuild OPT GAIN */ + dev->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE; + + + if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE) + { + if ((strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) + || (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)) + dev->opt[OPT_GRAY_GAIN].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + dev->opt[OPT_RED_GAIN].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GREEN_GAIN].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_BLUE_GAIN].cap &= ~SANE_CAP_INACTIVE; + } + } + + return SANE_STATUS_GOOD; + } + } + } + + + DBG (2, "control_option: unknown action %d \n", action); + return SANE_STATUS_INVAL; +} + + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Umax_PP_Device *dev = handle; + int dpi, remain; + + memset (&(dev->params), 0, sizeof (dev->params)); + DBG (64, "sane_get_parameters\n"); + + /* color/gray */ + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) != 0) + { + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) != 0) + dev->color = UMAX_PP_MODE_LINEART; + else + dev->color = UMAX_PP_MODE_GRAYSCALE; + } + else + dev->color = UMAX_PP_MODE_COLOR; + + /* offset control */ + if (dev->val[OPT_MANUAL_OFFSET].w == SANE_TRUE) + { + if (dev->color != UMAX_PP_MODE_COLOR) + { + dev->red_offset = 0; + dev->green_offset = (int) (dev->val[OPT_GRAY_OFFSET].w); + dev->blue_offset = 0; + } + else + { + dev->red_offset = (int) (dev->val[OPT_RED_OFFSET].w); + dev->green_offset = (int) (dev->val[OPT_GREEN_OFFSET].w); + dev->blue_offset = (int) (dev->val[OPT_BLUE_OFFSET].w); + } + } + else + { + dev->red_offset = 6; + dev->green_offset = 6; + dev->blue_offset = 6; + } + + /* gain control */ + if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE) + { + if (dev->color != UMAX_PP_MODE_COLOR) + { + dev->red_gain = 0; + dev->green_gain = (int) (dev->val[OPT_GRAY_GAIN].w); + dev->blue_gain = 0; + } + else + { + dev->red_gain = (int) (dev->val[OPT_RED_GAIN].w); + dev->green_gain = (int) (dev->val[OPT_GREEN_GAIN].w); + dev->blue_gain = (int) (dev->val[OPT_BLUE_GAIN].w); + } + } + else + { + dev->red_gain = red_gain; + dev->green_gain = green_gain; + dev->blue_gain = blue_gain; + } + + /* geometry */ + dev->TopX = dev->val[OPT_TL_X].w; + dev->TopY = dev->val[OPT_TL_Y].w; + dev->BottomX = dev->val[OPT_BR_X].w; + dev->BottomY = dev->val[OPT_BR_Y].w; + + /* resolution : only have 75, 150, 300, 600 and 1200 */ + dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w)); + if (dpi <= 75) + dpi = 75; + else if (dpi <= 150) + dpi = 150; + else if (dpi <= 300) + dpi = 300; + else if (dpi <= 600) + dpi = 600; + else + dpi = 1200; + dev->dpi = dpi; + + DBG (16, "sane_get_parameters: dpi set to %d\n", dpi); + + /* for highest resolutions , width must be aligned on 32 bit word */ + if (dpi >= 600) + { + remain = (dev->BottomX - dev->TopX) & 0x03; + if (remain) + { + DBG (64, "sane_get_parameters: %d-%d -> remain is %d\n", + dev->BottomX, dev->TopX, remain); + if (dev->BottomX + remain < dev->desc->max_h_size) + dev->BottomX += remain; + else + { + remain -= (dev->desc->max_h_size - dev->BottomX); + dev->BottomX = dev->desc->max_h_size; + dev->TopX -= remain; + } + } + } + + if (dev->val[OPT_PREVIEW].w == SANE_TRUE) + { + + if (dev->val[OPT_GRAY_PREVIEW].w == SANE_TRUE) + { + DBG (16, "sane_get_parameters: gray preview\n"); + dev->color = UMAX_PP_MODE_GRAYSCALE; + dev->params.format = SANE_FRAME_GRAY; + } + else + { + DBG (16, "sane_get_parameters: color preview\n"); + dev->color = UMAX_PP_MODE_COLOR; + dev->params.format = SANE_FRAME_RGB; + } + + dev->dpi = 75; + dev->TopX = 0; + dev->TopY = 0; + dev->BottomX = dev->desc->max_h_size; + dev->BottomY = dev->desc->max_v_size; + } + + + /* fill params */ + dev->params.last_frame = SANE_TRUE; + dev->params.lines = + ((dev->BottomY - dev->TopY) * dev->dpi) / dev->desc->ccd_res; + if (dev->dpi >= dev->desc->ccd_res) + dpi = dev->desc->ccd_res; + else + dpi = dev->dpi; + dev->params.pixels_per_line = + ((dev->BottomX - dev->TopX) * dpi) / dev->desc->ccd_res; + if (dev->color == UMAX_PP_MODE_COLOR) + { + dev->params.bytes_per_line = dev->params.pixels_per_line * 3; + dev->params.format = SANE_FRAME_RGB; + } + else + { + dev->params.bytes_per_line = dev->params.pixels_per_line; + dev->params.format = SANE_FRAME_GRAY; + } + dev->params.depth = 8; + + /* success */ + if (params != NULL) + memcpy (params, &(dev->params), sizeof (dev->params)); + return SANE_STATUS_GOOD; + +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Umax_PP_Device *dev = handle; + int rc, autoset; + int delta = 0, points; + + /* sanity check */ + if (dev->state == UMAX_PP_STATE_SCANNING) + { + DBG (2, "sane_start: device is already scanning\n"); + DEBUG (); + + return SANE_STATUS_DEVICE_BUSY; + } + + /* if cancelled, check if head is back home */ + if (dev->state == UMAX_PP_STATE_CANCELLED) + { + DBG (2, "sane_start: checking if scanner is parking head .... \n"); + + rc = sanei_umax_pp_status (); + points = 0; + + /* check if scanner busy parking */ + /* if so, wait parking completion */ + DBG (2, "sane_start: scanner busy\n"); + while ((rc == UMAX1220P_BUSY) && (points < 30)) + { + sleep (1); + rc = sanei_umax_pp_status (); + points++; + } + /* timeout waiting for scanner */ + if (rc == UMAX1220P_BUSY) + { + DBG (2, "sane_start: scanner still busy\n"); + return SANE_STATUS_DEVICE_BUSY; + } + dev->state = UMAX_PP_STATE_IDLE; + } + + + /* get values from options */ + sane_get_parameters (handle, NULL); + + /* sets lamp flag to TRUE */ + dev->val[OPT_LAMP_CONTROL].w = SANE_TRUE; + + /* tests if we do auto setting */ + if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE) + autoset = 0; + else + autoset = 1; + + + /* call start scan */ + if (dev->color == UMAX_PP_MODE_COLOR) + { + delta = umax_pp_get_sync (dev->dpi); + points = 2 * delta; + /* first lines are 'garbage' for 610P */ + if (sanei_umax_pp_getastra () < 1210) + points *= 2; + DBG (64, "sane_start:umax_pp_start(%d,%d,%d,%d,%d,1,%X,%X)\n", + dev->TopX, + dev->TopY - points, + dev->BottomX - dev->TopX, + dev->BottomY - dev->TopY + points, + dev->dpi, + (dev->red_gain << 8) + (dev->green_gain << 4) + + dev->blue_gain, + (dev->red_offset << 8) + (dev->green_offset << 4) + + dev->blue_offset); + + rc = sanei_umax_pp_start (dev->TopX, + dev->TopY - points, + dev->BottomX - dev->TopX, + dev->BottomY - dev->TopY + points, + dev->dpi, + 2, + autoset, + (dev->red_gain << 8) | + (dev->green_gain << 4) | + dev->blue_gain, + (dev->red_offset << 8) | + (dev->green_offset << 4) | + dev->blue_offset, &(dev->bpp), &(dev->tw), + &(dev->th)); + /* we enlarged the scanning zone */ + /* to allow reordering, we must */ + /* substract it from real scanning */ + /* zone */ + dev->th -= points; + DBG (64, "sane_start: bpp=%d,tw=%d,th=%d\n", dev->bpp, dev->tw, + dev->th); + } + else + { + DBG (64, "sane_start:umax_pp_start(%d,%d,%d,%d,%d,0,%X,%X)\n", + dev->TopX, + dev->TopY, + dev->BottomX - dev->TopX, + dev->BottomY - dev->TopY, dev->dpi, dev->gray_gain << 4, + dev->gray_offset << 4); + rc = sanei_umax_pp_start (dev->TopX, + dev->TopY, + dev->BottomX - dev->TopX, + dev->BottomY - dev->TopY, + dev->dpi, + 1, + autoset, + dev->gray_gain << 4, + dev->gray_offset << 4, &(dev->bpp), + &(dev->tw), &(dev->th)); + DBG (64, "sane_start: bpp=%d,tw=%d,th=%d\n", dev->bpp, dev->tw, + dev->th); + } + + if (rc != UMAX1220P_OK) + { + DBG (2, "sane_start: failure\n"); + return SANE_STATUS_IO_ERROR; + } + + /* scan started, no bytes read */ + dev->state = UMAX_PP_STATE_SCANNING; + dev->buflen = 0; + dev->bufread = 0; + dev->read = 0; + + /* leading lines for 610P aren't complete in color mode */ + /* and should be discarded */ + if ((sanei_umax_pp_getastra () < 1210) + && (dev->color == UMAX_PP_MODE_COLOR)) + { + rc = + sanei_umax_pp_read (2 * delta * dev->tw * dev->bpp, dev->tw, dev->dpi, + 0, + dev->buf + UMAX_PP_RESERVE - + 2 * delta * dev->tw * dev->bpp); + if (rc != UMAX1220P_OK) + { + DBG (2, "sane_start: first lines discarding failed\n"); + return SANE_STATUS_IO_ERROR; + } + } + + /* in case of color, we have to preload blue and green */ + /* data to allow reordering while later read */ + if ((dev->color == UMAX_PP_MODE_COLOR) && (delta > 0)) + { + rc = + sanei_umax_pp_read (2 * delta * dev->tw * dev->bpp, dev->tw, dev->dpi, + 0, + dev->buf + UMAX_PP_RESERVE - + 2 * delta * dev->tw * dev->bpp); + if (rc != UMAX1220P_OK) + { + DBG (2, "sane_start: preload buffer failed\n"); + return SANE_STATUS_IO_ERROR; + } + } + + /* OK .... */ + return SANE_STATUS_GOOD; + +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, + SANE_Int * len) +{ + Umax_PP_Device *dev = handle; + long int length; + int last, rc; + int x, y, nl, ll; + SANE_Byte *lbuf; + int max = 0; + int min = 255; + int delta = 0; + + + /* no data until further notice */ + *len = 0; + DBG (64, "sane_read(max_len=%d)\n", max_len); + ll = dev->tw * dev->bpp; + + /* sanity check */ + if (dev->state == UMAX_PP_STATE_CANCELLED) + { + DBG (2, "sane_read: scan cancelled\n"); + DEBUG (); + + return SANE_STATUS_CANCELLED; + } + + /* eof test */ + if (dev->read >= dev->th * ll) + { + DBG (2, "sane_read: end of scan reached\n"); + return SANE_STATUS_EOF; + } + + /* read data from scanner if needed */ + if ((dev->buflen == 0) || (dev->bufread >= dev->buflen)) + { + DBG (64, "sane_read: reading data from scanner\n"); + /* absolute number of bytes needed */ + length = ll * dev->th - dev->read; + + /* does all fit in a single last read ? */ + if (length <= dev->bufsize) + { + last = 1; + } + else + { + last = 0; + /* round number of scan lines */ + length = (dev->bufsize / ll) * ll; + } + + + if (dev->color == UMAX_PP_MODE_COLOR) + { + delta = umax_pp_get_sync (dev->dpi); + rc = + sanei_umax_pp_read (length, dev->tw, dev->dpi, last, + dev->buf + UMAX_PP_RESERVE); + } + else + rc = sanei_umax_pp_read (length, dev->tw, dev->dpi, last, dev->buf); + if (rc != UMAX1220P_OK) + return SANE_STATUS_IO_ERROR; + dev->buflen = length; + DBG (64, "sane_read: got %ld bytes of data from scanner\n", length); + + /* we transform data for software lineart */ + if (dev->color == UMAX_PP_MODE_LINEART) + { + DBG (64, "sane_read: software lineart\n"); + + for (y = 0; y < length; y++) + { + if (dev->buf[y] > max) + max = dev->buf[y]; + if (dev->buf[y] < min) + min = dev->buf[y]; + } + max = (min + max) / 2; + for (y = 0; y < length; y++) + { + if (dev->buf[y] > max) + dev->buf[y] = 255; + else + dev->buf[y] = 0; + } + } + else if (dev->color == UMAX_PP_MODE_COLOR) + { + /* number of lines */ + nl = dev->buflen / ll; + DBG (64, "sane_read: reordering %ld bytes of data (lines=%d)\n", + length, nl); + lbuf = (SANE_Byte *) malloc (dev->bufsize + UMAX_PP_RESERVE); + if (lbuf == NULL) + { + DBG (1, "sane_read: couldn't allocate %ld bytes\n", + dev->bufsize + UMAX_PP_RESERVE); + return SANE_STATUS_NO_MEM; + } + /* reorder data in R,G,B values */ + for (y = 0; y < nl; y++) + { + for (x = 0; x < dev->tw; x++) + { + switch (sanei_umax_pp_getastra ()) + { + case 610: + /* green value: sync'ed */ + lbuf[x * dev->bpp + y * ll + 1 + UMAX_PP_RESERVE] = + dev->buf[x + y * ll + 2 * dev->tw + UMAX_PP_RESERVE]; + + /* blue value, +delta line ahead of sync */ + lbuf[x * dev->bpp + y * ll + 2 + UMAX_PP_RESERVE] = + dev->buf[x + (y - delta) * ll + dev->tw + + UMAX_PP_RESERVE]; + + /* red value, +2*delta line ahead of sync */ + lbuf[x * dev->bpp + y * ll + UMAX_PP_RESERVE] = + dev->buf[x + (y - 2 * delta) * ll + UMAX_PP_RESERVE]; + + break; + default: + /* red value: sync'ed */ + lbuf[x * dev->bpp + y * ll + UMAX_PP_RESERVE] = + dev->buf[x + y * ll + 2 * dev->tw + UMAX_PP_RESERVE]; + + /* green value, +delta line ahead of sync */ + lbuf[x * dev->bpp + y * ll + 1 + UMAX_PP_RESERVE] = + dev->buf[x + (y - delta) * ll + dev->tw + + UMAX_PP_RESERVE]; + + /* blue value, +2*delta line ahead of sync */ + lbuf[x * dev->bpp + y * ll + 2 + UMAX_PP_RESERVE] = + dev->buf[x + (y - 2 * delta) * ll + UMAX_PP_RESERVE]; + } + } + } + /* store last data lines for next reordering */ + if (!last) + memcpy (lbuf + UMAX_PP_RESERVE - 2 * delta * ll, + dev->buf + UMAX_PP_RESERVE + dev->buflen - 2 * delta * ll, + 2 * delta * ll); + free (dev->buf); + dev->buf = lbuf; + } + dev->bufread = 0; + } + + /* how much get data we can get from memory buffer */ + length = dev->buflen - dev->bufread; + DBG (64, "sane_read: %ld bytes of data available\n", length); + if (length > max_len) + length = max_len; + + + + if (dev->color == UMAX_PP_MODE_COLOR) + memcpy (buf, dev->buf + dev->bufread + UMAX_PP_RESERVE, length); + else + memcpy (buf, dev->buf + dev->bufread, length); + *len = length; + dev->bufread += length; + dev->read += length; + DBG (64, "sane_read: %ld bytes read\n", length); + + return SANE_STATUS_GOOD; + +} + +void +sane_cancel (SANE_Handle handle) +{ + Umax_PP_Device *dev = handle; + int rc; + + DBG (64, "sane_cancel\n"); + if (dev->state == UMAX_PP_STATE_IDLE) + { + DBG (3, "cancel: cancelling idle \n"); + return; + } + if (dev->state == UMAX_PP_STATE_SCANNING) + { + DBG (3, "cancel: stopping current scan\n"); + + dev->buflen = 0; + + dev->state = UMAX_PP_STATE_CANCELLED; + sanei_umax_pp_cancel (); + } + else + { + /* STATE_CANCELLED */ + DBG (2, "cancel: checking if scanner is still parking head .... \n"); + + rc = sanei_umax_pp_status (); + + /* check if scanner busy parking */ + if (rc == UMAX1220P_BUSY) + { + DBG (2, "cancel: scanner busy\n"); + return; + } + dev->state = UMAX_PP_STATE_IDLE; + } +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + DBG (129, "unused arg: handle = %p, non_blocking = %d\n", + handle, (int) non_blocking); + + DBG (2, "set_io_mode: not supported\n"); + + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + + DBG (129, "unused arg: handle = %p, fd = %p\n", handle, (void *) fd); + + DBG (2, "get_select_fd: not supported\n"); + + return SANE_STATUS_UNSUPPORTED; +} |