diff options
Diffstat (limited to 'backend/mustek_pp.c')
-rw-r--r-- | backend/mustek_pp.c | 1958 |
1 files changed, 1958 insertions, 0 deletions
diff --git a/backend/mustek_pp.c b/backend/mustek_pp.c new file mode 100644 index 0000000..cd86bb9 --- /dev/null +++ b/backend/mustek_pp.c @@ -0,0 +1,1958 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2000-2003 Jochen Eisinger <jochen.eisinger@gmx.net> + 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 Mustek PP flatbed scanners. */ + +#include "../include/sane/config.h" + +#if defined(HAVE_STDLIB_H) +# include <stdlib.h> +#endif +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#if defined(HAVE_STRING_H) +# include <string.h> +#elif defined(HAVE_STRINGS_H) +# include <strings.h> +#endif +#if defined(HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include <math.h> +#include <fcntl.h> +#include <time.h> +#if defined(HAVE_SYS_TIME_H) +# include <sys/time.h> +#endif +#if defined(HAVE_SYS_TYPES_H) +# include <sys/types.h> +#endif +#include <sys/wait.h> + +#define BACKEND_NAME mustek_pp + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" + +#include "../include/sane/sanei_backend.h" + +#include "../include/sane/sanei_config.h" +#define MUSTEK_PP_CONFIG_FILE "mustek_pp.conf" + +#include "../include/sane/sanei_pa4s2.h" + +#include "mustek_pp.h" +#include "mustek_pp_drivers.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +/* converts millimeter to pixels at a given resolution */ +#define MM_TO_PIXEL(mm, dpi) (((float )mm * 5.0 / 127.0) * (float)dpi) + /* and back */ +#define PIXEL_TO_MM(pixel, dpi) (((float )pixel / (float )dpi) * 127.0 / 5.0) + +/* if you change the source, please set MUSTEK_PP_STATE to "devel". Do *not* + * change the MUSTEK_PP_BUILD. */ +#define MUSTEK_PP_BUILD 13 +#define MUSTEK_PP_STATE "beta" + + +/* auth callback... since basic user authentication is done by saned, this + * callback mechanism isn't used */ +SANE_Auth_Callback sane_auth; + +/* count of present devices */ +static int num_devices = 0; + +/* list of present devices */ +static Mustek_pp_Device *devlist = NULL; + +/* temporary array of configuration options used during device attachment */ +static Mustek_pp_config_option *cfgoptions = NULL; +static int numcfgoptions = 0; + +/* list of pointers to the SANE_Device structures of the Mustek_pp_Devices */ +static SANE_Device **devarray = NULL; + +/* currently active Handles */ +static Mustek_pp_Handle *first_hndl = NULL; + +static SANE_String_Const mustek_pp_modes[4] = {SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL}; +static SANE_Word mustek_pp_modes_size = 10; + +static SANE_String_Const mustek_pp_speeds[6] = {"Slowest", "Slower", "Normal", "Faster", "Fastest", NULL}; +static SANE_Word mustek_pp_speeds_size = 8; +static SANE_Word mustek_pp_depths[5] = {4, 8, 10, 12, 16}; + +/* prototypes */ +static void free_cfg_options(int *numoptions, Mustek_pp_config_option** options); +static SANE_Status do_eof(Mustek_pp_Handle *hndl); +static SANE_Status do_stop(Mustek_pp_Handle *hndl); +static int reader_process (Mustek_pp_Handle * hndl, int pipe); +static SANE_Status sane_attach(SANE_String_Const port, SANE_String_Const name, + SANE_Int driver, SANE_Int info); +static void init_options(Mustek_pp_Handle *hndl); +static void attach_device(SANE_String *driver, SANE_String *name, + SANE_String *port, SANE_String *option_ta); + + +/* + * Auxiliary function for freeing arrays of configuration options, + */ +static void +free_cfg_options(int *numoptions, Mustek_pp_config_option** options) +{ + int i; + if (*numoptions) + { + for (i=0; i<*numoptions; ++i) + { + free ((*options)[i].name); + free ((*options)[i].value); + } + free (*options); + } + *options = NULL; + *numoptions = 0; +} + +/* do_eof: + * closes the pipeline + * + * ChangeLog: + * + * Description: + * closes the pipe (read-only end) + */ +static SANE_Status +do_eof (Mustek_pp_Handle *hndl) +{ + if (hndl->pipe >= 0) { + + close (hndl->pipe); + hndl->pipe = -1; + } + + return SANE_STATUS_EOF; +} + +/* do_stop: + * ends the reader_process and stops the scanner + * + * ChangeLog: + * + * Description: + * kills the reader process with a SIGTERM and cancels the scanner + */ +static SANE_Status +do_stop(Mustek_pp_Handle *hndl) +{ + + int exit_status; + + do_eof (hndl); + + if (hndl->reader > 0) { + + DBG (3, "do_stop: terminating reader process\n"); + kill (hndl->reader, SIGTERM); + + while (wait (&exit_status) != hndl->reader); + + DBG ((exit_status == SANE_STATUS_GOOD ? 3 : 1), + "do_stop: reader_process terminated with status ``%s''\n", + sane_strstatus(exit_status)); + hndl->reader = 0; + hndl->dev->func->stop (hndl); + + return exit_status; + + } + + hndl->dev->func->stop (hndl); + + return SANE_STATUS_GOOD; +} + +/* sigterm_handler: + * cancel scanner when receiving a SIGTERM + * + * ChangeLog: + * + * Description: + * just exit... reader_process takes care that nothing bad will happen + * + * EDG - Jan 14, 2004: + * Make sure that the parport is released again by the child process + * under all circumstances, because otherwise the parent process may no + * longer be able to claim it (they share the same file descriptor, and + * the kernel doesn't release the child's claim because the file + * descriptor isn't cleaned up). If that would happen, the lamp may stay + * on and may not return to its home position, unless the scanner + * frontend is restarted. + * (This happens only when sanei_pa4s2 uses libieee1284 AND + * libieee1284 goes via /dev/parportX). + * + */ +static int fd_to_release = 0; +/*ARGSUSED*/ +static RETSIGTYPE +sigterm_handler (int signal __UNUSED__) +{ + sanei_pa4s2_enable(fd_to_release, SANE_FALSE); + _exit (SANE_STATUS_GOOD); +} + +/* reader_process: + * receives data from the scanner and stuff it into the pipeline + * + * ChangeLog: + * + * Description: + * The signal handle for SIGTERM is initialized. + * + */ +static int +reader_process (Mustek_pp_Handle * hndl, int pipe) +{ + sigset_t sigterm_set; + struct SIGACTION act; + FILE *fp; + SANE_Status status; + int line; + int size, elem; + + SANE_Byte *buffer; + + sigemptyset (&sigterm_set); + sigaddset (&sigterm_set, SIGTERM); + + if (!(buffer = malloc (hndl->params.bytes_per_line))) + return SANE_STATUS_NO_MEM; + + if (!(fp = fdopen(pipe, "w"))) + return SANE_STATUS_IO_ERROR; + + fd_to_release = hndl->fd; + memset (&act, 0, sizeof(act)); + act.sa_handler = sigterm_handler; + sigaction (SIGTERM, &act, NULL); + + if ((status = hndl->dev->func->start (hndl)) != SANE_STATUS_GOOD) + return status; + + size = hndl->params.bytes_per_line; + elem = 1; + + for (line=0; line<hndl->params.lines ; line++) { + + sigprocmask (SIG_BLOCK, &sigterm_set, NULL); + + hndl->dev->func->read (hndl, buffer); + + if (getppid() == 1) { + /* The parent process has died. Stop the scan (to make + sure that the lamp is off and returns home). This is + a safety measure to make sure that we don't break + the scanner in case the frontend crashes. */ + DBG (1, "reader_process: front-end died; aborting.\n"); + hndl->dev->func->stop (hndl); + return SANE_STATUS_CANCELLED; + } + + sigprocmask (SIG_UNBLOCK, &sigterm_set, NULL); + + fwrite (buffer, size, elem, fp); + } + + fclose (fp); + + free (buffer); + + return SANE_STATUS_GOOD; +} + + + +/* sane_attach: + * adds a new entry to the Mustek_pp_Device *devlist list + * + * ChangeLog: + * + * Description: + * After memory for a new device entry is allocated, the + * parameters for the device are determined by a call to + * capabilities(). + * + * Afterwards the new device entry is inserted into the + * devlist + * + */ +static SANE_Status +sane_attach (SANE_String_Const port, SANE_String_Const name, SANE_Int driver, SANE_Int info) +{ + Mustek_pp_Device *dev; + + DBG (3, "sane_attach: attaching device ``%s'' to port %s (driver %s v%s by %s)\n", + name, port, Mustek_pp_Drivers[driver].driver, + Mustek_pp_Drivers[driver].version, + Mustek_pp_Drivers[driver].author); + + if ((dev = malloc (sizeof (Mustek_pp_Device))) == NULL) { + + DBG (1, "sane_attach: not enough free memory\n"); + return SANE_STATUS_NO_MEM; + + } + + memset (dev, 0, sizeof (Mustek_pp_Device)); + + memset (&dev->sane, 0, sizeof (SANE_Device)); + + dev->func = &Mustek_pp_Drivers[driver]; + + dev->sane.name = dev->name = strdup (name); + dev->port = strdup (port); + dev->info = info; /* Modified by EDG */ + + /* Transfer the options parsed from the configuration file */ + dev->numcfgoptions = numcfgoptions; + dev->cfgoptions = cfgoptions; + numcfgoptions = 0; + cfgoptions = NULL; + + dev->func->capabilities (info, &dev->model, &dev->vendor, &dev->type, + &dev->maxres, &dev->minres, &dev->maxhsize, &dev->maxvsize, + &dev->caps); + + dev->sane.model = dev->model; + dev->sane.vendor = dev->vendor; + dev->sane.type = dev->type; + + dev->next = devlist; + devlist = dev; + + num_devices++; + + return SANE_STATUS_GOOD; +} + + +/* init_options: + * Sets up the option descriptors for a device + * + * ChangeLog: + * + * Description: + */ +static void +init_options(Mustek_pp_Handle *hndl) +{ + int i; + + memset (hndl->opt, 0, sizeof (hndl->opt)); + memset (hndl->val, 0, sizeof (hndl->val)); + + for (i = 0; i < NUM_OPTIONS; ++i) + { + hndl->opt[i].size = sizeof (SANE_Word); + hndl->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + hndl->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; + hndl->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + hndl->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + hndl->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + hndl->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + hndl->val[OPT_NUM_OPTS].w = NUM_OPTIONS; + + /* "Mode" group: */ + + hndl->opt[OPT_MODE_GROUP].title = "Scan Mode"; + hndl->opt[OPT_MODE_GROUP].desc = ""; + hndl->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + hndl->opt[OPT_MODE_GROUP].cap = 0; + hndl->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + hndl->opt[OPT_MODE_GROUP].size = 0; + + /* scan mode */ + hndl->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + hndl->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + hndl->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + hndl->opt[OPT_MODE].type = SANE_TYPE_STRING; + hndl->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + hndl->opt[OPT_MODE].size = mustek_pp_modes_size; + hndl->opt[OPT_MODE].constraint.string_list = mustek_pp_modes; + hndl->val[OPT_MODE].s = strdup (mustek_pp_modes[2]); + + /* resolution */ + hndl->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + hndl->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + hndl->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + hndl->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED; + hndl->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + hndl->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_RESOLUTION].constraint.range = &hndl->dpi_range; + hndl->val[OPT_RESOLUTION].w = SANE_FIX (hndl->dev->minres); + hndl->dpi_range.min = SANE_FIX (hndl->dev->minres); + hndl->dpi_range.max = SANE_FIX (hndl->dev->maxres); + hndl->dpi_range.quant = SANE_FIX (1); + + /* speed */ + hndl->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; + hndl->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; + hndl->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; + hndl->opt[OPT_SPEED].type = SANE_TYPE_STRING; + hndl->opt[OPT_SPEED].size = mustek_pp_speeds_size; + hndl->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; + hndl->opt[OPT_SPEED].constraint.string_list = mustek_pp_speeds; + hndl->val[OPT_SPEED].s = strdup (mustek_pp_speeds[2]); + + if (! (hndl->dev->caps & CAP_SPEED_SELECT)) + hndl->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; + + /* preview */ + hndl->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + hndl->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + hndl->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + hndl->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + hndl->val[OPT_PREVIEW].w = SANE_FALSE; + + /* gray preview */ + hndl->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; + hndl->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; + hndl->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; + hndl->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; + hndl->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; + + /* color dept */ + hndl->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH; + hndl->opt[OPT_DEPTH].title = SANE_TITLE_BIT_DEPTH; + hndl->opt[OPT_DEPTH].desc = + "Number of bits per sample for color scans, typical values are 8 for truecolor (24bpp)" + "up to 16 for far-to-many-color (48bpp)."; + hndl->opt[OPT_DEPTH].type = SANE_TYPE_INT; + hndl->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; + hndl->opt[OPT_DEPTH].constraint.word_list = mustek_pp_depths; + hndl->opt[OPT_DEPTH].unit = SANE_UNIT_BIT; + hndl->opt[OPT_DEPTH].size = sizeof(SANE_Word); + hndl->val[OPT_DEPTH].w = 8; + + if ( !(hndl->dev->caps & CAP_DEPTH)) + hndl->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; + + + /* "Geometry" group: */ + + hndl->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; + hndl->opt[OPT_GEOMETRY_GROUP].desc = ""; + hndl->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + hndl->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; + hndl->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + hndl->opt[OPT_GEOMETRY_GROUP].size = 0; + + /* top-left x */ + hndl->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + hndl->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + hndl->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + hndl->opt[OPT_TL_X].type = SANE_TYPE_FIXED; + hndl->opt[OPT_TL_X].unit = SANE_UNIT_MM; + hndl->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_TL_X].constraint.range = &hndl->x_range; + hndl->x_range.min = SANE_FIX (0); + hndl->x_range.max = SANE_FIX (PIXEL_TO_MM(hndl->dev->maxhsize,hndl->dev->maxres)); + hndl->x_range.quant = 0; + hndl->val[OPT_TL_X].w = hndl->x_range.min; + + /* top-left y */ + hndl->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + hndl->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + hndl->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + hndl->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; + hndl->opt[OPT_TL_Y].unit = SANE_UNIT_MM; + hndl->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_TL_Y].constraint.range = &hndl->y_range; + hndl->y_range.min = SANE_FIX(0); + hndl->y_range.max = SANE_FIX(PIXEL_TO_MM(hndl->dev->maxvsize,hndl->dev->maxres)); + hndl->y_range.quant = 0; + hndl->val[OPT_TL_Y].w = hndl->y_range.min; + + /* bottom-right x */ + hndl->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + hndl->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + hndl->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + hndl->opt[OPT_BR_X].type = SANE_TYPE_FIXED; + hndl->opt[OPT_BR_X].unit = SANE_UNIT_MM; + hndl->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_BR_X].constraint.range = &hndl->x_range; + hndl->val[OPT_BR_X].w = hndl->x_range.max; + + /* bottom-right y */ + hndl->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + hndl->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + hndl->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + hndl->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; + hndl->opt[OPT_BR_Y].unit = SANE_UNIT_MM; + hndl->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_BR_Y].constraint.range = &hndl->y_range; + hndl->val[OPT_BR_Y].w = hndl->y_range.max; + + /* "Enhancement" group: */ + + hndl->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; + hndl->opt[OPT_ENHANCEMENT_GROUP].desc = ""; + hndl->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; + hndl->opt[OPT_ENHANCEMENT_GROUP].cap = 0; + hndl->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + hndl->opt[OPT_ENHANCEMENT_GROUP].size = 0; + + + /* custom-gamma table */ + hndl->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; + hndl->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; + hndl->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; + hndl->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; + hndl->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + if ( !(hndl->dev->caps & CAP_GAMMA_CORRECT)) + hndl->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + + /* grayscale gamma vector */ + hndl->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; + hndl->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; + hndl->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; + hndl->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; + hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; + hndl->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); + hndl->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_GAMMA_VECTOR].constraint.range = &hndl->gamma_range; + hndl->val[OPT_GAMMA_VECTOR].wa = &hndl->gamma_table[0][0]; + + /* red gamma vector */ + hndl->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; + hndl->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; + hndl->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; + hndl->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; + hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; + hndl->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); + hndl->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_GAMMA_VECTOR_R].constraint.range = &hndl->gamma_range; + hndl->val[OPT_GAMMA_VECTOR_R].wa = &hndl->gamma_table[1][0]; + + /* green gamma vector */ + hndl->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; + hndl->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; + hndl->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; + hndl->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; + hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; + hndl->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); + hndl->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_GAMMA_VECTOR_G].constraint.range = &hndl->gamma_range; + hndl->val[OPT_GAMMA_VECTOR_G].wa = &hndl->gamma_table[2][0]; + + /* blue gamma vector */ + hndl->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; + hndl->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; + hndl->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; + hndl->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; + hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; + hndl->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); + hndl->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; + hndl->opt[OPT_GAMMA_VECTOR_B].constraint.range = &hndl->gamma_range; + hndl->val[OPT_GAMMA_VECTOR_B].wa = &hndl->gamma_table[3][0]; + + hndl->gamma_range.min = 0; + hndl->gamma_range.max = 255; + hndl->gamma_range.quant = 1; + + hndl->opt[OPT_INVERT].name = SANE_NAME_NEGATIVE; + hndl->opt[OPT_INVERT].title = SANE_TITLE_NEGATIVE; + hndl->opt[OPT_INVERT].desc = SANE_DESC_NEGATIVE; + hndl->opt[OPT_INVERT].type = SANE_TYPE_BOOL; + hndl->val[OPT_INVERT].w = SANE_FALSE; + + if (! (hndl->dev->caps & CAP_INVERT)) + hndl->opt[OPT_INVERT].cap |= SANE_CAP_INACTIVE; + + +} + +/* attach_device: + * Attempts to attach a device to the list after parsing of a section + * of the configuration file. + * + * ChangeLog: + * + * Description: + * After parsing a scanner section of the config file, this function + * is called to look for a driver with a matching name. When found, + * this driver is called to initialize the device. + */ +static void +attach_device(SANE_String *driver, SANE_String *name, + SANE_String *port, SANE_String *option_ta) +{ + int found = 0, driver_no, port_no; + const char **ports; + + if (!strcmp (*port, "*")) + { + ports = sanei_pa4s2_devices(); + DBG (3, "sanei_init: auto probing port\n"); + } + else + { + ports = malloc (sizeof(char *) * 2); + ports[0] = *port; + ports[1] = NULL; + } + + for (port_no=0; ports[port_no] != NULL; port_no++) + { + for (driver_no=0 ; driver_no<MUSTEK_PP_NUM_DRIVERS ; driver_no++) + { + if (strcasecmp (Mustek_pp_Drivers[driver_no].driver, *driver) == 0) + { + Mustek_pp_Drivers[driver_no].init ( + (*option_ta == 0 ? CAP_NOTHING : CAP_TA), + ports[port_no], *name, sane_attach); + found = 1; + break; + } + } + } + + free (ports); + + if (found == 0) + { + DBG (1, "sane_init: no scanner detected\n"); + DBG (3, "sane_init: either the driver name ``%s'' is invalid, or no scanner was detected\n", *driver); + } + + free (*name); + free (*port); + free (*driver); + if (*option_ta) + free (*option_ta); + *name = *port = *driver = *option_ta = 0; + + /* In case of a successful initialization, the configuration options + should have been transfered to the device, but this function can + deal with that. */ + free_cfg_options(&numcfgoptions, &cfgoptions); +} + +/* sane_init: + * Reads configuration file and registers hardware driver + * + * ChangeLog: + * + * Description: + * in *version_code the SANE version this backend was compiled with and the + * version of the backend is returned. The value of authorize is stored in + * the global variable sane_auth. + * + * Next the configuration file is read. If it isn't present, all drivers + * are auto-probed with default values (port 0x378, with and without TA). + * + * The configuration file is expected to contain lines of the form + * + * scanner <name> <port> <driver> [<option_ta>] + * + * where <name> is a arbitrary name to identify this entry + * <port> is the port where the scanner is attached to + * <driver> is the name of the driver to use + * + * if the optional argument "option_ta" is present the driver uses special + * parameters fitting for a trasparency adapter. + */ + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + FILE *fp; + char config_line[1024]; + const char *config_line_ptr; + int line=0, driver_no; + char *driver = 0, *port = 0, *name = 0, *option_ta = 0; + + DBG_INIT (); + DBG (3, "sane-mustek_pp, version 0.%d-%s. build for SANE %s\n", + MUSTEK_PP_BUILD, MUSTEK_PP_STATE, VERSION); + DBG (3, "backend by Jochen Eisinger <jochen.eisinger@gmx.net>\n"); + + if (version_code != NULL) + *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, MUSTEK_PP_BUILD); + + sane_auth = authorize; + + + fp = sanei_config_open (MUSTEK_PP_CONFIG_FILE); + + if (fp == NULL) + { + char driver_name[64]; + const char **devices = sanei_pa4s2_devices(); + int device_no; + + DBG (2, "sane_init: could not open configuration file\n"); + + for (device_no = 0; devices[device_no] != NULL; device_no++) + { + DBG (3, "sane_init: trying ``%s''\n", devices[device_no]); + for (driver_no=0 ; driver_no<MUSTEK_PP_NUM_DRIVERS ; driver_no++) + { + Mustek_pp_Drivers[driver_no].init(CAP_NOTHING, devices[device_no], + Mustek_pp_Drivers[driver_no].driver, sane_attach); + + snprintf (driver_name, 64, "%s-ta", + Mustek_pp_Drivers[driver_no].driver); + + Mustek_pp_Drivers[driver_no].init(CAP_TA, devices[device_no], + driver_name, sane_attach); + } + } + + free (devices); + return SANE_STATUS_GOOD; + } + + while (sanei_config_read (config_line, 1023, fp)) + { + line++; + if ((!*config_line) || (*config_line == '#')) + continue; + + config_line_ptr = config_line; + + if (strncmp(config_line_ptr, "scanner", 7) == 0) + { + config_line_ptr += 7; + + if (name) + { + /* Parsing of previous scanner + options is finished. Attach + the device before we parse the next section. */ + attach_device(&driver, &name, &port, &option_ta); + } + + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + if (!*config_line_ptr) + { + DBG (1, "sane_init: parse error in line %d after ``scanner''\n", + line); + continue; + } + + config_line_ptr = sanei_config_get_string (config_line_ptr, &name); + if ((name == NULL) || (!*name)) + { + DBG (1, "sane_init: parse error in line %d after ``scanner''\n", + line); + if (name != NULL) + free (name); + name = 0; + continue; + } + + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + if (!*config_line_ptr) + { + DBG (1, "sane_init: parse error in line %d after " + "``scanner %s''\n", line, name); + free (name); + name = 0; + continue; + } + + config_line_ptr = sanei_config_get_string (config_line_ptr, &port); + if ((port == NULL) || (!*port)) + { + DBG (1, "sane_init: parse error in line %d after " + "``scanner %s''\n", line, name); + free (name); + name = 0; + if (port != NULL) + free (port); + port = 0; + continue; + } + + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + if (!*config_line_ptr) + { + DBG (1, "sane_init: parse error in line %d after " + "``scanner %s %s''\n", line, name, port); + free (name); + free (port); + name = 0; + port = 0; + continue; + } + + config_line_ptr = sanei_config_get_string (config_line_ptr, &driver); + if ((driver == NULL) || (!*driver)) + { + DBG (1, "sane_init: parse error in line %d after " + "``scanner %s %s''\n", line, name, port); + free (name); + name = 0; + free (port); + port = 0; + if (driver != NULL) + free (driver); + driver = 0; + continue; + } + + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + + if (*config_line_ptr) + { + config_line_ptr = sanei_config_get_string (config_line_ptr, + &option_ta); + + if ((option_ta == NULL) || (!*option_ta) || + (strcasecmp (option_ta, "use_ta") != 0)) + { + DBG (1, "sane_init: parse error in line %d after " + "``scanner %s %s %s''\n", line, name, port, driver); + free (name); + free (port); + free (driver); + if (option_ta) + free (option_ta); + name = port = driver = option_ta = 0; + continue; + } + } + + if (*config_line_ptr) + { + DBG (1, "sane_init: parse error in line %d after " + "``scanner %s %s %s %s\n", line, name, port, driver, + (option_ta == 0 ? "" : option_ta)); + free (name); + free (port); + free (driver); + if (option_ta) + free (option_ta); + name = port = driver = option_ta = 0; + continue; + } + } + else if (strncmp(config_line_ptr, "option", 6) == 0) + { + /* Format for options: option <name> [<value>] + Note that the value is optional. */ + char *optname, *optval = 0; + Mustek_pp_config_option *tmpoptions; + + config_line_ptr += 6; + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + if (!*config_line_ptr) + { + DBG (1, "sane_init: parse error in line %d after ``option''\n", + line); + continue; + } + + config_line_ptr = sanei_config_get_string (config_line_ptr, &optname); + if ((optname == NULL) || (!*optname)) + { + DBG (1, "sane_init: parse error in line %d after ``option''\n", + line); + if (optname != NULL) + free (optname); + continue; + } + + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + if (*config_line_ptr) + { + /* The option has a value. + No need to check the value; that's up to the backend */ + config_line_ptr = sanei_config_get_string (config_line_ptr, + &optval); + + config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); + } + + if (*config_line_ptr) + { + DBG (1, "sane_init: parse error in line %d after " + "``option %s %s''\n", line, optname, + (optval == 0 ? "" : optval)); + free (optname); + if (optval) + free (optval); + continue; + } + + if (!strcmp (optname, "no_epp")) + { + u_int pa4s2_options; + if (name) + DBG (2, "sane_init: global option found in local scope, " + "executing anyway\n"); + free (optname); + if (optval) + { + DBG (1, "sane_init: unexpected value for option no_epp\n"); + free (optval); + continue; + } + DBG (3, "sane_init: disabling mode EPP\n"); + sanei_pa4s2_options (&pa4s2_options, SANE_FALSE); + pa4s2_options |= SANEI_PA4S2_OPT_NO_EPP; + sanei_pa4s2_options (&pa4s2_options, SANE_TRUE); + continue; + } + else if (!name) + { + DBG (1, "sane_init: parse error in line %d: unexpected " + " ``option''\n", line); + free (optname); + if (optval) + free (optval); + continue; + } + + + /* Extend the (global) array of options */ + tmpoptions = realloc(cfgoptions, + (numcfgoptions+1)*sizeof(cfgoptions[0])); + if (!tmpoptions) + { + DBG (1, "sane_init: not enough memory for device options\n"); + free_cfg_options(&numcfgoptions, &cfgoptions); + return SANE_STATUS_NO_MEM; + } + + cfgoptions = tmpoptions; + cfgoptions[numcfgoptions].name = optname; + cfgoptions[numcfgoptions].value = optval; + ++numcfgoptions; + } + else + { + DBG (1, "sane_init: parse error at beginning of line %d\n", line); + continue; + } + + } + + /* If we hit the end of the file, we still may have to process the + last driver */ + if (name) + attach_device(&driver, &name, &port, &option_ta); + + fclose(fp); + return SANE_STATUS_GOOD; + +} + +/* sane_exit: + * Unloads all drivers and frees allocated memory + * + * ChangeLog: + * + * Description: + * All open devices are closed first. Then all registered devices + * are removed. + * + */ + +void +sane_exit (void) +{ + Mustek_pp_Handle *hndl; + Mustek_pp_Device *dev; + + if (first_hndl) + DBG (3, "sane_exit: closing open devices\n"); + + while (first_hndl) + { + hndl = first_hndl; + sane_close (hndl); + } + + dev = devlist; + num_devices = 0; + devlist = NULL; + + while (dev) { + + free (dev->port); + free (dev->name); + free (dev->vendor); + free (dev->model); + free (dev->type); + free_cfg_options (&dev->numcfgoptions, &dev->cfgoptions); + dev = dev->next; + + } + + if (devarray != NULL) + free (devarray); + devarray = NULL; + + DBG (3, "sane_exit: all drivers unloaded\n"); + +} + +/* sane_get_devices: + * Returns a list of registered devices + * + * ChangeLog: + * + * Description: + * A possible present old device_list is removed first. A new + * devarray is allocated and filled with pointers to the + * SANE_Device structures of the Mustek_pp_Devices + */ +/*ARGSUSED*/ +SANE_Status +sane_get_devices (const SANE_Device *** device_list, + SANE_Bool local_only __UNUSED__) +{ + int ctr; + Mustek_pp_Device *dev; + + if (devarray != NULL) + free (devarray); + + devarray = malloc ((num_devices + 1) * sizeof (devarray[0])); + + if (devarray == NULL) + { + DBG (1, "sane_get_devices: not enough memory for device list\n"); + return SANE_STATUS_NO_MEM; + } + + dev = devlist; + + for (ctr=0 ; ctr<num_devices ; ctr++) { + devarray[ctr] = &dev->sane; + dev = dev->next; + } + + devarray[num_devices] = NULL; + *device_list = (const SANE_Device **)devarray; + + return SANE_STATUS_GOOD; +} + +/* sane_open: + * opens a device and prepares it for operation + * + * ChangeLog: + * + * Description: + * The device identified by ``devicename'' is looked + * up in the list, or if devicename is zero, the + * first device from the list is taken. + * + * open is called for the selected device. + * + * The handel is set up with default values, and the + * option descriptors are initialized + */ + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + + Mustek_pp_Handle *hndl; + Mustek_pp_Device *dev; + SANE_Status status; + int fd, i; + + if (devicename[0]) { + + dev = devlist; + + while (dev) { + + if (strcmp (dev->name, devicename) == 0) + break; + + dev = dev->next; + + } + + if (!dev) { + + DBG (1, "sane_open: unknown devicename ``%s''\n", devicename); + return SANE_STATUS_INVAL; + + } + } else + dev = devlist; + + if (!dev) { + DBG (1, "sane_open: no devices present...\n"); + return SANE_STATUS_INVAL; + } + + DBG (3, "sane_open: Using device ``%s'' (driver %s v%s by %s)\n", + dev->name, dev->func->driver, dev->func->version, dev->func->author); + + if ((hndl = malloc (sizeof (Mustek_pp_Handle))) == NULL) { + + DBG (1, "sane_open: not enough free memory for the handle\n"); + return SANE_STATUS_NO_MEM; + + } + + if ((status = dev->func->open (dev->port, dev->caps, &fd)) != SANE_STATUS_GOOD) { + + DBG (1, "sane_open: could not open device (%s)\n", + sane_strstatus (status)); + return status; + + } + + hndl->next = first_hndl; + hndl->dev = dev; + hndl->fd = fd; + hndl->state = STATE_IDLE; + hndl->pipe = -1; + + init_options (hndl); + + dev->func->setup (hndl); + + /* Initialize driver-specific configuration options. This must be + done after calling the setup() function because only then the + driver is guaranteed to be fully initialized */ + for (i = 0; i<dev->numcfgoptions; ++i) + { + status = dev->func->config (hndl, + dev->cfgoptions[i].name, + dev->cfgoptions[i].value); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "sane_open: could not set option %s for device (%s)\n", + dev->cfgoptions[i].name, sane_strstatus (status)); + + /* Question: should the initialization be aborted when an + option cannot be handled ? + The driver should have reasonable built-in defaults, so + an illegal option value or an unknown option should not + be fatal. Therefore, it's probably ok to ignore the error. */ + } + } + + first_hndl = hndl; + + *handle = hndl; + + return SANE_STATUS_GOOD; +} + +/* sane_close: + * closes a given device and frees all resources + * + * ChangeLog: + * + * Description: + * The handle is searched in the list of active handles. + * If it's found, the handle is removed. + * + * If the associated device is still scanning, the process + * is cancelled. + * + * Then the backend makes sure, the lamp was at least + * 2 seconds on. + * + * Afterwards the selected handel is closed + */ +void +sane_close (SANE_Handle handle) +{ + Mustek_pp_Handle *prev, *hndl; + + prev = NULL; + + for (hndl = first_hndl; hndl; hndl = hndl->next) + { + if (hndl == handle) + break; + prev = hndl; + } + + if (hndl == NULL) + { + DBG (2, "sane_close: unknown device handle\n"); + return; + } + + if (hndl->state == STATE_SCANNING) { + sane_cancel (handle); + do_eof (handle); + } + + if (prev != NULL) + prev->next = hndl->next; + else + first_hndl = hndl->next; + + DBG (3, "sane_close: maybe waiting for lamp...\n"); + if (hndl->lamp_on) + while (time (NULL) - hndl->lamp_on < 2) + sleep (1); + + hndl->dev->func->close (hndl); + + DBG (3, "sane_close: device closed\n"); + + free (handle); + +} + +/* sane_get_option_descriptor: + * does what it says + * + * ChangeLog: + * + * Description: + * + */ + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Mustek_pp_Handle *hndl = handle; + + if ((unsigned) option >= NUM_OPTIONS) + { + DBG (2, "sane_get_option_descriptor: option %d doesn't exist\n", option); + return NULL; + } + + return hndl->opt + option; +} + + +/* sane_control_option: + * Reads or writes an option + * + * ChangeLog: + * + * Desription: + * If a pointer to info is given, the value is initialized to zero + * while scanning options cannot be read or written. next a basic + * check whether the request is valid is done. + * + * Depending on ``action'' the value of the option is either read + * (in the first block) or written (in the second block). auto + * values aren't supported. + * + * before a value is written, some checks are performed. Depending + * on the option, that is written, other options also change + * + */ +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + Mustek_pp_Handle *hndl = handle; + SANE_Status status; + SANE_Word w, cap; + + if (info) + *info = 0; + + if (hndl->state == STATE_SCANNING) + { + DBG (2, "sane_control_option: device is scanning\n"); + return SANE_STATUS_DEVICE_BUSY; + } + + if ((unsigned int) option >= NUM_OPTIONS) + { + DBG (2, "sane_control_option: option %d doesn't exist\n", option); + return SANE_STATUS_INVAL; + } + + cap = hndl->opt[option].cap; + + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + DBG (2, "sane_control_option: option %d isn't active\n", option); + return SANE_STATUS_INVAL; + } + + if (action == SANE_ACTION_GET_VALUE) + { + + switch (option) + { + /* word options: */ + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + 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_INVERT: + case OPT_DEPTH: + + *(SANE_Word *) val = hndl->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, hndl->val[option].wa, hndl->opt[option].size); + return SANE_STATUS_GOOD; + + /* string options: */ + case OPT_MODE: + case OPT_SPEED: + + strcpy (val, hndl->val[option].s); + return SANE_STATUS_GOOD; + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + + if (!SANE_OPTION_IS_SETTABLE (cap)) + { + DBG (2, "sane_control_option: option can't be set (%s)\n", + hndl->opt[option].name); + return SANE_STATUS_INVAL; + } + + status = sanei_constrain_value (hndl->opt + option, val, info); + + if (status != SANE_STATUS_GOOD) + { + DBG (2, "sane_control_option: constrain_value failed (%s)\n", + sane_strstatus (status)); + return status; + } + + switch (option) + { + /* (mostly) side-effect-free word options: */ + case OPT_RESOLUTION: + case OPT_TL_X: + case OPT_BR_X: + case OPT_TL_Y: + case OPT_BR_Y: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + case OPT_INVERT: + case OPT_DEPTH: + + if (info) + *info |= SANE_INFO_RELOAD_PARAMS; + + hndl->val[option].w = *(SANE_Word *) val; + 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 (hndl->val[option].wa, val, hndl->opt[option].size); + return SANE_STATUS_GOOD; + + /* side-effect-free string options: */ + case OPT_SPEED: + + if (hndl->val[option].s) + free (hndl->val[option].s); + + hndl->val[option].s = strdup (val); + return SANE_STATUS_GOOD; + + + /* options with side-effects: */ + + case OPT_CUSTOM_GAMMA: + w = *(SANE_Word *) val; + + if (w == hndl->val[OPT_CUSTOM_GAMMA].w) + return SANE_STATUS_GOOD; /* no change */ + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS; + + hndl->val[OPT_CUSTOM_GAMMA].w = w; + + if (w == SANE_TRUE) + { + const char *mode = hndl->val[OPT_MODE].s; + + if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + else + { + hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + } + + return SANE_STATUS_GOOD; + + case OPT_MODE: + { + char *old_val = hndl->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; + + hndl->val[option].s = strdup (val); + + hndl->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + + hndl->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; + + if ((hndl->dev->caps & CAP_DEPTH) && (strcmp(val, SANE_VALUE_SCAN_MODE_COLOR) == 0)) + hndl->opt[OPT_DEPTH].cap &= ~SANE_CAP_INACTIVE; + + if (!(hndl->dev->caps & CAP_GAMMA_CORRECT)) + return SANE_STATUS_GOOD; + + if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) != 0) + hndl->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + + if (hndl->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) + { + if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) + hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + hndl->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + + return SANE_STATUS_GOOD; + } + } + } + + DBG (2, "sane_control_option: unknown action\n"); + return SANE_STATUS_INVAL; +} + + +/* sane_get_parameters: + * returns the set of parameters, that is used for the next scan + * + * ChangeLog: + * + * Description: + * + * First of all it is impossible to change the parameter set + * while scanning. + * + * sane_get_parameters not only returns the parameters for + * the next scan, it also sets them, i.e. converts the + * options in actuall parameters. + * + * The following parameters are set: + * + * scanmode: according to the option SCANMODE, but + * 24bit color, if PREVIEW is selected and + * grayscale if GRAY_PREVIEW is selected + * depth: the bit depth for color modes (if + * supported) or 24bit by default + * (ignored in bw/grayscale or if not + * supported) + * dpi: resolution + * invert: if supported else defaults to false + * gamma: if supported and selected + * ta: if supported by the device + * speed: selected speed (or fastest if not + * supported) + * scanarea: the scanarea is calculated from the + * selections the user has mode. note + * that the area may slightly differ from + * the scanarea selected due to rounding + * note also, that a scanarea of + * (0,0)-(100,100) will include all pixels + * where 0 <= x < 100 and 0 <= y < 100 + * afterwards, all values are copied into the SANE_Parameters + * structure. + */ +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Mustek_pp_Handle *hndl = handle; + char *mode; + int dpi, ctr; + + if (hndl->state != STATE_SCANNING) + { + + + memset (&hndl->params, 0, sizeof (hndl->params)); + + + if ((hndl->dev->caps & CAP_DEPTH) && (hndl->mode == MODE_COLOR)) + hndl->depth = hndl->val[OPT_DEPTH].w; + else + hndl->depth = 8; + + dpi = (int) (SANE_UNFIX (hndl->val[OPT_RESOLUTION].w) + 0.5); + + hndl->res = dpi; + + if (hndl->dev->caps & CAP_INVERT) + hndl->invert = hndl->val[OPT_INVERT].w; + else + hndl->invert = SANE_FALSE; + + if (hndl->dev->caps & CAP_TA) + hndl->use_ta = SANE_TRUE; + else + hndl->use_ta = SANE_FALSE; + + if ((hndl->dev->caps & CAP_GAMMA_CORRECT) && (hndl->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)) + hndl->do_gamma = SANE_TRUE; + else + hndl->do_gamma = SANE_FALSE; + + if (hndl->dev->caps & CAP_SPEED_SELECT) { + + for (ctr=SPEED_SLOWEST; ctr<=SPEED_FASTEST; ctr++) + if (strcmp(mustek_pp_speeds[ctr], hndl->val[OPT_SPEED].s) == 0) + hndl->speed = ctr; + + + + } else + hndl->speed = SPEED_NORMAL; + + mode = hndl->val[OPT_MODE].s; + + if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) + hndl->mode = MODE_BW; + else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + hndl->mode = MODE_GRAYSCALE; + else + hndl->mode = MODE_COLOR; + + if (hndl->val[OPT_PREVIEW].w == SANE_TRUE) + { + + hndl->speed = SPEED_FASTEST; + hndl->depth = 8; + if (! hndl->use_ta) + hndl->invert = SANE_FALSE; + hndl->do_gamma = SANE_FALSE; + + if (hndl->val[OPT_GRAY_PREVIEW].w == SANE_TRUE) + hndl->mode = MODE_GRAYSCALE; + else { + hndl->mode = MODE_COLOR; + } + + } + + hndl->topX = + MIN ((int) + (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_TL_X].w), hndl->dev->maxres) + + 0.5), hndl->dev->maxhsize); + hndl->topY = + MIN ((int) + (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_TL_Y].w), hndl->dev->maxres) + + 0.5), hndl->dev->maxvsize); + + hndl->bottomX = + MIN ((int) + (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_BR_X].w), hndl->dev->maxres) + + 0.5), hndl->dev->maxhsize); + hndl->bottomY = + MIN ((int) + (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_BR_Y].w), hndl->dev->maxres) + + 0.5), hndl->dev->maxvsize); + + /* If necessary, swap the upper and lower boundaries to avoid negative + distances. */ + if (hndl->topX > hndl->bottomX) { + SANE_Int tmp = hndl->topX; + hndl->topX = hndl->bottomX; + hndl->bottomX = tmp; + } + if (hndl->topY > hndl->bottomY) { + SANE_Int tmp = hndl->topY; + hndl->topY = hndl->bottomY; + hndl->bottomY = tmp; + } + + hndl->params.pixels_per_line = (hndl->bottomX - hndl->topX) * hndl->res + / hndl->dev->maxres; + + hndl->params.bytes_per_line = hndl->params.pixels_per_line; + + switch (hndl->mode) + { + + case MODE_BW: + hndl->params.bytes_per_line /= 8; + + if ((hndl->params.pixels_per_line % 8) != 0) + hndl->params.bytes_per_line++; + + hndl->params.depth = 1; + break; + + case MODE_GRAYSCALE: + hndl->params.depth = 8; + hndl->params.format = SANE_FRAME_GRAY; + break; + + case MODE_COLOR: + hndl->params.depth = hndl->depth; + hndl->params.bytes_per_line *= 3; + if (hndl->depth > 8) + hndl->params.bytes_per_line *= 2; + hndl->params.format = SANE_FRAME_RGB; + break; + + } + + hndl->params.last_frame = SANE_TRUE; + + hndl->params.lines = (hndl->bottomY - hndl->topY) * hndl->res / + hndl->dev->maxres; + } + else + DBG (2, "sane_get_parameters: can't set parameters while scanning\n"); + + if (params != NULL) + *params = hndl->params; + + return SANE_STATUS_GOOD; + +} + + +/* sane_start: + * starts the scan. data aquisition will start immedially + * + * ChangeLog: + * + * Description: + * + */ +SANE_Status +sane_start (SANE_Handle handle) +{ + Mustek_pp_Handle *hndl = handle; + int pipeline[2]; + + if (hndl->state == STATE_SCANNING) { + DBG (2, "sane_start: device is already scanning\n"); + return SANE_STATUS_DEVICE_BUSY; + + } + + sane_get_parameters (hndl, NULL); + + if (pipe(pipeline) < 0) { + DBG (1, "sane_start: could not initialize pipe (%s)\n", + strerror(errno)); + return SANE_STATUS_IO_ERROR; + } + + hndl->reader = fork(); + + if (hndl->reader == 0) { + + sigset_t ignore_set; + struct SIGACTION act; + + close (pipeline[0]); + + sigfillset (&ignore_set); + sigdelset (&ignore_set, SIGTERM); + sigprocmask (SIG_SETMASK, &ignore_set, NULL); + + memset (&act, 0, sizeof(act)); + sigaction (SIGTERM, &act, NULL); + + _exit (reader_process (hndl, pipeline[1])); + + } + + close (pipeline[1]); + + hndl->pipe = pipeline[0]; + + hndl->state = STATE_SCANNING; + + return SANE_STATUS_GOOD; + +} + + +/* sane_read: + * receives data from pipeline and passes it to the caller + * + * ChangeLog: + * + * Description: + * ditto + */ +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, + SANE_Int * len) +{ + Mustek_pp_Handle *hndl = handle; + SANE_Int nread; + + + if (hndl->state == STATE_CANCELLED) { + DBG (2, "sane_read: device already cancelled\n"); + do_eof (hndl); + hndl->state = STATE_IDLE; + return SANE_STATUS_CANCELLED; + } + + if (hndl->state != STATE_SCANNING) { + DBG (1, "sane_read: device isn't scanning\n"); + return SANE_STATUS_INVAL; + } + + + *len = nread = 0; + + while (*len < max_len) { + + nread = read(hndl->pipe, buf + *len, max_len - *len); + + if (hndl->state == STATE_CANCELLED) { + + *len = 0; + DBG(3, "sane_read: scan was cancelled\n"); + + do_eof (hndl); + hndl->state = STATE_IDLE; + return SANE_STATUS_CANCELLED; + + } + + if (nread < 0) { + + if (errno == EAGAIN) { + + if (*len == 0) + DBG(3, "sane_read: no data at the moment\n"); + else + DBG(3, "sane_read: %d bytes read\n", *len); + + return SANE_STATUS_GOOD; + + } else { + + DBG(1, "sane_read: IO error (%s)\n", strerror(errno)); + + hndl->state = STATE_IDLE; + do_stop(hndl); + + do_eof (hndl); + + *len = 0; + return SANE_STATUS_IO_ERROR; + + } + } + + *len += nread; + + if (nread == 0) { + + if (*len == 0) { + + DBG (3, "sane_read: read finished\n"); + do_stop(hndl); + + hndl->state = STATE_IDLE; + + return do_eof(hndl); + + } + + DBG(3, "sane_read: read last buffer of %d bytes\n", + *len); + + return SANE_STATUS_GOOD; + + } + + } + + DBG(3, "sane_read: read full buffer of %d bytes\n", *len); + + return SANE_STATUS_GOOD; +} + + +/* sane_cancel: + * stops a scan and ends the reader process + * + * ChangeLog: + * + * Description: + * + */ +void +sane_cancel (SANE_Handle handle) +{ + Mustek_pp_Handle *hndl = handle; + + if (hndl->state != STATE_SCANNING) + return; + + hndl->state = STATE_CANCELLED; + + do_stop (hndl); + +} + + +/* sane_set_io_mode: + * toggles between blocking and non-blocking reading + * + * ChangeLog: + * + * Description: + * + */ +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + + Mustek_pp_Handle *hndl=handle; + + if (hndl->state != STATE_SCANNING) + return SANE_STATUS_INVAL; + + + if (fcntl (hndl->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) { + + DBG(1, "sane_set_io_mode: can't set io mode\n"); + + return SANE_STATUS_IO_ERROR; + + } + + return SANE_STATUS_GOOD; +} + + +/* sane_get_select_fd: + * returns the pipeline fd for direct reading + * + * ChangeLog: + * + * Description: + * to allow the frontend to receive the data directly it + * can read from the pipeline itself + */ +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + Mustek_pp_Handle *hndl=handle; + + if (hndl->state != STATE_SCANNING) + return SANE_STATUS_INVAL; + + *fd = hndl->pipe; + + return SANE_STATUS_GOOD; +} + +/* include drivers */ +#include "mustek_pp_decl.h" +#include "mustek_pp_null.c" +#include "mustek_pp_cis.h" +#include "mustek_pp_cis.c" +#include "mustek_pp_ccd300.h" +#include "mustek_pp_ccd300.c" |