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