kopia lustrzana https://gitlab.com/sane-project/backends
1959 wiersze
51 KiB
C
1959 wiersze
51 KiB
C
/* 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 void
|
|
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"
|