diff options
Diffstat (limited to 'backend/canon_pp.c')
| -rw-r--r-- | backend/canon_pp.c | 2039 | 
1 files changed, 2039 insertions, 0 deletions
| diff --git a/backend/canon_pp.c b/backend/canon_pp.c new file mode 100644 index 0000000..cc7c27e --- /dev/null +++ b/backend/canon_pp.c @@ -0,0 +1,2039 @@ +/* sane - Scanner Access Now Easy. +   Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix +   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. + +   ----- + +   canon_pp.c: $Revision$ + +   This file is part of the canon_pp backend, supporting Canon FBX30P  +   and NX40P scanners +   */ + +#ifdef  _AIX +#include  <lalloca.h>		/* MUST come first for AIX! */ +#endif + +#define BACKEND_NAME canon_pp + +#define THREE_BITS 0xE0 +#define TWO_BITS 0xC0 +#define MM_PER_IN 25.4 + +#ifndef NOSANE +#include "../include/sane/config.h" +#endif + +#ifndef VERSION +#define VERSION "$Revision$" +#endif + +#include  <string.h> +#include  <math.h> +#include  <unistd.h> +#include  <sys/stat.h> +#include  <sys/types.h> +#include  <stdlib.h> +#include  <errno.h> +#include  <ieee1284.h> + +#include  "../include/sane/sane.h" +#include  "../include/sane/saneopts.h" + +#include "canon_pp-dev.h" +#include "canon_pp-io.h" +#include "canon_pp.h" + +/* #include  "../include/sane/sanei_pio.h" */ +#include  "../include/sane/sanei_config.h" +#include  "../include/sane/sanei_backend.h" +/* #include  "../include/sane/sanei_debug.h" */ + + +/* Prototypes */ +static SANE_Status init_device(struct parport *pp); + +/* create a calibration file and give it initial values */ +static int init_cal(char *file); + +static SANE_Status fix_weights_file(CANONP_Scanner *cs); + +static SANE_Status detect_mode(CANONP_Scanner *cs); + +/* Global Variables (ack!) */ + +/* The first device in a linked list of devices */ +static CANONP_Scanner *first_dev = NULL; +/* The default scanner to open */ +static char *def_scanner = NULL; +/* The number of devices */ +static int num_devices = 0; +/* ieee1284 parallel ports */ +struct parport_list pl; +/* leftover from the last read */ +static SANE_Byte *read_leftover = NULL; +/* leftover from the last read */ +static SANE_Bool force_nibble = SANE_FALSE; + +/* Constants */ + +/* Colour Modes */ +static const SANE_String_Const cmodes[] = {  +	SANE_VALUE_SCAN_MODE_GRAY,  +	SANE_VALUE_SCAN_MODE_COLOR,  +	NULL }; + +/* bit depths */ +static const SANE_String_Const depths[] = { "8", "12", NULL }; +/* resolutions */ +static const SANE_Int res300[] = {3, 75, 150, 300}; +static const SANE_Int res600[] = {4, 75, 150, 300, 600}; + + +/************************************************************************* + * + * sane_init() + * + * Initialises data for the list of scanners, stored in canon-p.conf. + * + * Scanners are not sent any commands until sane_open() is called. + * + *************************************************************************/ +	SANE_Status +sane_init (SANE_Int *vc, SANE_Auth_Callback cb) +{ +	SANE_Status status = SANE_STATUS_GOOD; +	int i, tmp;  +	int tmp_im = INITMODE_AUTO; +	FILE *fp; +	char line[81]; /* plus 1 for a null */ +	char *tmp_wf, *tmp_port; +	CANONP_Scanner *s_tmp; + + +	DBG_INIT(); + +#if defined PACKAGE && defined VERSION +	DBG(2, ">> sane_init (version %s null, authorize %s null): " PACKAGE " " VERSION "\n",  +	    (vc) ? "!=" : "==", (cb) ? "!=" : "=="); +#endif + +	if(vc) +		*vc = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0); + +	DBG(2,"sane_init: >> ieee1284_find_ports\n"); +	/* Find lp ports */ +	tmp = ieee1284_find_ports(&pl, 0); +	DBG(2,"sane_init: %d << ieee1284_find_ports\n", tmp); + +	if (tmp != E1284_OK) +	{ +		DBG(1,"sane_init: Error trying to get port list\n"); +		return SANE_STATUS_IO_ERROR; +	} + + +	if (pl.portc < 1) +	{ +		DBG(1,"sane_init: Error, no parallel ports found.\n"); +		return SANE_STATUS_IO_ERROR; +	} + +	DBG(10,"sane_init: %i parallel port(s) found.\n", pl.portc); +	/* Setup data structures for each port */ +	for(i=0; i<pl.portc; i++) +	{ +		DBG(10,"sane_init: port %s\n", pl.portv[i]->name); +		status = init_device(pl.portv[i]); +		/* Now's a good time to quit if we got an error */ +		if (status != SANE_STATUS_GOOD) return status; +	} + +	/* This should never be true here */ +	if (num_devices == 0) +		status = SANE_STATUS_IO_ERROR; + +	/* just to be extra sure, the line will always have an end: */ +	line[sizeof(line)-1] = '\0'; + +	/*  +	 * Read information from config file: pixel weight location and default  +	 * port. +	 */ +	if((fp = sanei_config_open(CANONP_CONFIG_FILE))) +	{ +		while(sanei_config_read(line, sizeof (line) - 1, fp)) +		{ +			DBG(100, "sane_init: >%s<\n", line); +			if(line[0] == '#')	/* ignore line comments */ +				continue; +			if(!strlen(line)) +				continue;	/* ignore empty lines */ + +			if(strncmp(line,"calibrate ", 10) == 0) +			{ +				/* warning: pointer trickyness ahead  +				 * Do not free tmp_port! */ +				DBG(40, "sane_init: calibrate line, %s\n",  +						line); +				tmp_wf = strdup(line+10); +				tmp_port = strstr(tmp_wf, " "); +				if ((tmp_port == tmp_wf) || (tmp_port == NULL)) +				{ +					/* They have used an old style config  +					 * file which does not specify scanner +					 * Assume first port */ +					DBG(1, "sane_init: old config line:" +							"\"%s\".  Please add " +							"a port argument.\n",  +							line); + +					/* first_dev should never be null here +					 * because we found at least one  +					 * parallel port above */ +					first_dev->weights_file = tmp_wf; +					DBG(100, "sane_init: Successfully " +							"parsed (old) cal, " +							"weight file is " +							"'%s'.\n", tmp_wf); +					continue; + +				} + +				/* Now find which scanner wants  +				 * this calibration file */ +				s_tmp = first_dev; +				DBG(100, "sane_init: Finding scanner on port " +						"'%s'\n", tmp_port+1); +				while (s_tmp != NULL) +				{ +					if (!strcmp(s_tmp->params.port->name, +								tmp_port+1)) +					{ +						DBG(100, "sane_init: Found!\n"); +						/* Now terminate the weight  +						 * file string */ +						*tmp_port = '\0'; +						s_tmp->weights_file = tmp_wf; +						DBG(100, "sane_init: Parsed " +								"cal, for port" +								" '%s', weight" +								" file is '%s'" +								".\n",  +								s_tmp->params. +								  port->name, +								tmp_wf); +						break; +					} +					s_tmp = s_tmp->next; +				} +				if (s_tmp == NULL) +				{ +					/* we made it all the way through the +					 * list and didn't find the port */ +					free(tmp_wf); +					DBG(10, "sane_init: calibrate line is " +							"for unknown port!\n"); +				} +				continue; +			} + + +			if(strncmp(line,"ieee1284 ", 9) == 0) +			{ +				DBG(100, "sane_init: Successfully parsed " +						"default scanner.\n"); +				/* this will be our default scanner */ +				def_scanner = strdup(line+9); +				continue; +			} + +			if(strncmp(line,"force_nibble", 12) == 0) +			{ +				DBG(100, "sane_init: force_nibble " +						"requested.\n"); +				force_nibble = SANE_TRUE; +				continue; +			} + +			if(strncmp(line,"init_mode ", 10) == 0) +			{ + +				/* parse what sort of initialisation mode to  +				 * use */ +				if (strncmp(line+10, "FB620P", 6) == 0) +					tmp_im = INITMODE_20P; +				else if (strncmp(line+10, "FB630P", 6) == 0) +					tmp_im = INITMODE_30P; +				else if (strncmp(line+10, "AUTO", 4) == 0) +					tmp_im = INITMODE_AUTO; + +				/* now work out which port it blongs to */ + +				tmp_port = strstr(line+10, " "); + +				if (tmp_port == NULL) +				{ +					/* first_dev should never be null here +					 * because we found at least one  +					 * parallel port above */ +					first_dev->init_mode = tmp_im; +					DBG(100, "sane_init: Parsed init-1.\n"); +					continue; +				} + + +				s_tmp = first_dev; +				while (s_tmp != NULL) +				{ +					if (!strcmp(s_tmp->params.port->name, +								tmp_port+1)) +					{ +						s_tmp->init_mode = tmp_im; +						DBG(100, "sane_init: Parsed " +								"init.\n"); +						break; +					} +					s_tmp = s_tmp->next; +				} +				if (s_tmp == NULL) +				{ +					/* we made it all the way through the +					 * list and didn't find the port */ +					DBG(10, "sane_init: init_mode line is " +							"for unknown port!\n"); +				} + +				continue; +			} +			DBG(1, "sane_init: Unknown configuration command!"); + +		}  +		fclose (fp); +	}	 + +	/* There should now be a LL of ports starting at first_dev */ + +	for (s_tmp = first_dev; s_tmp != NULL; s_tmp = s_tmp->next) +	{ +		/* Assume there's no scanner present until proven otherwise */ +		s_tmp->scanner_present = SANE_FALSE; + +		/* Try to detect if there's a scanner there, and if so,  +		 * what sort of scanner it is */ +		status = detect_mode(s_tmp); + +		if (status != SANE_STATUS_GOOD)  +		{ +			DBG(10,"sane_init: Error detecting port mode on %s!\n", +					s_tmp->params.port->name); +			s_tmp->scanner_present = SANE_FALSE; +			continue; +		}  +		 +		/* detect_mode suceeded, so the port is open.  This beholdens +		 * us to call ieee1284_close in any of the remaining error +		 * cases in this loop. */ +#if 0 +		tmp = sanei_canon_pp_detect(s_tmp->params.port,  +				s_tmp->init_mode); + + +		if (tmp && (s_tmp->ieee1284_mode != M1284_NIBBLE)) +		{ +			/* A failure, try again in nibble mode... */ +			DBG(1, "sane_init: Failed on ECP mode, falling " +					"back to nibble mode\n"); + +			s_tmp->ieee1284_mode = M1284_NIBBLE; +			sanei_canon_pp_set_ieee1284_mode(s_tmp->ieee1284_mode); +			tmp = sanei_canon_pp_detect(s_tmp->params.port,  +					s_tmp->init_mode); +		} +		/* still no go? */ +		if (tmp) +		{ +			DBG(1,"sane_init: couldn't find a scanner on port " +					"%s\n", s_tmp->params.port->name); + +			ieee1284_close(s_tmp->params.port); +			continue; +		} +		 +#endif +		/* all signs point to yes, try it out */ +		if (ieee1284_claim(s_tmp->params.port) != E1284_OK) { +			DBG(10, "sane_init: Couldn't claim port %s.\n", +				s_tmp->params.port->name); + +			ieee1284_close(s_tmp->params.port); +			continue; +		} +		 +		DBG(2, "sane_init: >> initialise\n"); +		tmp = sanei_canon_pp_initialise(&(s_tmp->params),  +				s_tmp->init_mode); +		DBG(2, "sane_init: << %d initialise\n", tmp); +		if (tmp) { +			DBG(10, "sane_init: Couldn't contact scanner on port " +				"%s. Probably no scanner there?\n", +				s_tmp->params.port->name); +			ieee1284_release(s_tmp->params.port); +			ieee1284_close(s_tmp->params.port); +			s_tmp->scanner_present = SANE_FALSE; +			continue; +		} + +		/* put it back to sleep until we're ready to  +		 * open for business again - this will only work +		 * if we actually have a scanner there! */ +		DBG(100, "sane_init: And back to sleep again\n"); +		sanei_canon_pp_sleep_scanner(s_tmp->params.port); + +		/* leave the port open but not claimed - this is regardless  +		 * of the return value of initialise */ +		ieee1284_release(s_tmp->params.port); + +		/* Finally, we're sure there's a scanner there! Now we +		 * just have to load the weights file...*/ + +		if (fix_weights_file(s_tmp) != SANE_STATUS_GOOD) { +			DBG(1, "sane_init: Eeek! fix_weights_file failed for " +				"scanner on port %s!\n",  +				s_tmp->params.port->name); +			/* non-fatal.. scans will look ugly as sin unless +			 * they calibrate */ +		} + +		/* Cocked, locked and ready to rock */ +		s_tmp->hw.model = s_tmp->params.name; +		s_tmp->scanner_present = SANE_TRUE; +	} + +	DBG(2, "<< sane_init\n"); + +	return status; +} + + +/************************************************************************* + * + * sane_get_devices() + * + * Gives a list of devices avaialable.  In our case, that's the linked + * list produced by sane_init. + * + *************************************************************************/ +	SANE_Status +sane_get_devices (const SANE_Device ***dl, SANE_Bool local) +{	 +	static const SANE_Device **devlist; +	CANONP_Scanner *dev; +	int i; + +	DBG(2, ">> sane_get_devices (%p, %d)\n", (const void*)dl, local); + +	if (dl == NULL) +	{ +		DBG(1, "sane_get_devices: ERROR: devlist pointer is NULL!"); +		return SANE_STATUS_INVAL; +	} + +	if (devlist != NULL) +	{ +		/* this has been called already */ +		*dl = devlist; +		return SANE_STATUS_GOOD; +	} +	devlist = malloc((num_devices + 1) * sizeof(*devlist)); +	if (devlist == NULL) +		return SANE_STATUS_NO_MEM; + +	i = 0; +	for (dev = first_dev; dev != NULL; dev = dev->next) +	{ +		if (dev->scanner_present == SANE_TRUE) +		{ +			devlist[i] = &(dev->hw); +			i++; +		} +	} + +	devlist[i] = NULL; + +	*dl = devlist; + +	DBG(2, "<< sane_get_devices\n"); +	return SANE_STATUS_GOOD; +} + + +/************************************************************************* + * + * sane_open() + * + * Open the scanner described by name.  Ask libieee1284 to claim the port + * and call Simon's init code.  Also configure data structures. + * + *************************************************************************/ +	SANE_Status +sane_open (SANE_String_Const name, SANE_Handle *h) +{ +	CANONP_Scanner *cs; +	SANE_Range *tmp_range; +	int tmp; + +	DBG(2, ">> sane_open (h=%p, name=\"%s\")\n", (void *)h, name); + +	if ((h == NULL) || (name == NULL))  +	{ +		DBG(2,"sane_open: Null pointer received!\n"); +		return SANE_STATUS_INVAL; +	} + +	if (!strlen(name)) +	{ +		DBG(10,"sane_open: Empty name given, assuming first/" +				"default scanner\n"); +		if (def_scanner == NULL) +			name = first_dev->params.port->name; +		else +			name = def_scanner; + +		/* we don't _have_ to fit this name, so _don't_ fail if it's +		 * not there */ + +		cs = first_dev; +		while((cs != NULL) && strcmp(cs->params.port->name, name)) +			cs = cs->next; + +		/* if we didn't find the port they want, or there's no scanner  +		 * there, we just want to find _any_ scanner */ +		if ((cs == NULL) || (cs->scanner_present != SANE_TRUE)) +		{ +			cs = first_dev; +			while((cs != NULL) &&  +					(cs->scanner_present == SANE_FALSE)) +				cs = cs->next; +		} + +	} else { + +		/* they're dead keen for this name, so _do_ fail if it's +		 * not there */ +		cs = first_dev; +		while((cs != NULL) && strcmp(cs->params.port->name, name)) +			cs = cs->next; +	} + + +	if (cs == NULL)  +	{ +		DBG(2,"sane_open: No scanner found or requested port " +				"doesn't exist (%s)\n", name); +		return SANE_STATUS_IO_ERROR; +	} +	if (cs->scanner_present == SANE_FALSE) +	{ +		DBG(1,"sane_open: Request to open port with no scanner " +				"(%s)\n", name); +		return SANE_STATUS_IO_ERROR; +	} +	if (cs->opened == SANE_TRUE)  +	{ +		DBG(2,"sane_open; Oi!, That scanner's already open.\n"); +		return SANE_STATUS_DEVICE_BUSY; +	} + +	/* If the scanner has already been opened once, we don't have to do  +	 * this setup again */ +	if (cs->setup == SANE_TRUE)  +	{ +		cs->opened = SANE_TRUE; +		*h = (SANE_Handle)cs; +		return SANE_STATUS_GOOD; +	} + +	tmp = ieee1284_claim(cs->params.port); +	if (tmp != E1284_OK) { +		DBG(1, "sane_open: Could not claim port!\n"); +		return SANE_STATUS_IO_ERROR; +	} + +	/* I put the scanner to sleep before, better wake it back up */ + +	DBG(2, "sane_open: >> initialise\n"); +	tmp = sanei_canon_pp_initialise(&(cs->params), cs->init_mode); +	DBG(2, "sane_open: << %d initialise\n", tmp); +	if (tmp != 0) { +		DBG(1, "sane_open: initialise returned %d, something is " +				"wrong with the scanner!\n", tmp); + +		DBG(1, "sane_open: Can't contact scanner.  Try power " +				"cycling scanner, and unplug any " +				"printers\n"); +		ieee1284_release(cs->params.port); +		return SANE_STATUS_IO_ERROR; +	} + +	if (cs->weights_file != NULL) +		DBG(2, "sane_open: >> load_weights(%s, %p)\n",  +				cs->weights_file,  +				(const void *)(&(cs->params))); +	else +		DBG(2, "sane_open: >> load_weights(NULL, %p)\n",  +				(const void *)(&(cs->params))); +	tmp = sanei_canon_pp_load_weights(cs->weights_file, &(cs->params)); +	DBG(2, "sane_open: << %d load_weights\n", tmp); + +	if (tmp != 0) { +		DBG(1, "sane_open: WARNING: Error on load_weights: " +				"returned %d.  This could be due to a corrupt " +				"calibration file.  Try recalibrating and if " +				"problems persist, please report the problem " +				"to the canon_pp maintainer\n", tmp); +		cs->cal_valid = SANE_FALSE; +	} else { +		cs->cal_valid = SANE_TRUE; +		DBG(10, "sane_open: loadweights successful, uploading gamma" +				" profile...\n"); +		tmp = sanei_canon_pp_adjust_gamma(&(cs->params)); +		if (tmp != 0) +			DBG(1, "sane_open: WARNING: adjust_gamma returned " +					"%d!\n", tmp); + +		DBG(10, "sane_open: after adjust_gamma Status = %i\n",  +				sanei_canon_pp_check_status(cs->params.port)); +	} +		 + +	/* Configure ranges etc */ + +	/* Resolution - determined by magic number */ + +	if (cs->params.scanheadwidth == 2552) +		cs->opt[OPT_RESOLUTION].constraint.word_list = res300; +	else +		cs->opt[OPT_RESOLUTION].constraint.word_list = res600; + + +	/* TL-X */ +	if(!(tmp_range = malloc(sizeof(*tmp_range)))) +		return SANE_STATUS_NO_MEM; +	(*tmp_range).min = 0; +	(*tmp_range).max = 215; +	cs->opt[OPT_TL_X].constraint.range = tmp_range; + +	/* TL-Y */ +	if(!(tmp_range = malloc(sizeof(*tmp_range)))) +		return SANE_STATUS_NO_MEM; +	(*tmp_range).min = 0; +	(*tmp_range).max = 296; +	cs->opt[OPT_TL_Y].constraint.range = tmp_range; + +	/* BR-X */ +	if(!(tmp_range = malloc(sizeof(*tmp_range)))) +		return SANE_STATUS_NO_MEM; +	(*tmp_range).min = 3; +	(*tmp_range).max = 216; +	cs->opt[OPT_BR_X].constraint.range = tmp_range; + +	/* BR-Y */ +	if(!(tmp_range = malloc(sizeof(*tmp_range)))) +		return SANE_STATUS_NO_MEM; +	(*tmp_range).min = 1; +	(*tmp_range).max = 297; +	cs->opt[OPT_BR_Y].constraint.range = tmp_range; + + +	cs->opened = SANE_TRUE; +	cs->setup = SANE_TRUE; + +	*h = (SANE_Handle)cs; + +	DBG(2, "<< sane_open\n"); + +	return SANE_STATUS_GOOD; +} + +/************************************************************************* + * + * sane_get_option_descriptor() + * + * Return the structure for option number opt. + * + *************************************************************************/ +	const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle h, SANE_Int opt) +{ +	CANONP_Scanner *cs = ((CANONP_Scanner *)h); +	/*DBG(2, ">> sane_get_option_descriptor (h=%p, opt=%d)\n", h, opt);*/ + +	if (h == NULL) { +		DBG(10,"sane_get_option_descriptor: WARNING: h==NULL!\n"); +		return NULL; +	} + +	if ((unsigned)opt >= NUM_OPTIONS) { +		DBG(10,"sane_get_option_descriptor: Note: opt >= " +				"NUM_OPTIONS!\n"); +		return NULL; +	} + +	if (cs->opened == SANE_FALSE) +	{ +		DBG(1,"sane_get_option_descriptor: That scanner (%p) ain't " +				"open yet\n", h); +		return NULL; +	} + +	/*DBG(2, "<< sane_get_option_descriptor\n");*/ + +	return (cs->opt + opt); +} + + +/************************************************************************* + * + * sane_control_option() + * + * Set a value for one of the options provided. + * + *************************************************************************/ +SANE_Status +sane_control_option (SANE_Handle h, SANE_Int opt, SANE_Action act, +		void *val, SANE_Word *info) +{ +	CANONP_Scanner *cs = ((CANONP_Scanner *)h); +	int i = 0, tmp, maxresi; + +	DBG(2, ">> sane_control_option (h=%p, opt=%d, act=%d)\n", +			h,opt,act);  +	/* Do some sanity checks on the parameters  +	 * note that val can be null for buttons */ +	if ((h == NULL) || ((val == NULL) && (opt != OPT_CAL)))    +		/* || (info == NULL))  - Don't check this any more..  +		 * frontends seem to like passing a null */ +	{ +		DBG(1,"sane_control_option: Frontend passed me a null! " +				"(h=%p,val=%p,info=%p)\n",(void*)h, +				val,(void*)info); +		return SANE_STATUS_INVAL; +	} + +	if (((unsigned)opt) >= NUM_OPTIONS)  +	{ +		DBG(1,"sane_control_option: I don't do option %d.\n", opt); +		return SANE_STATUS_INVAL; +	} + +	if (cs->opened == SANE_FALSE) +	{ +		DBG(1,"sane_control_option: That scanner (%p) ain't " +				"open yet\n", h); +		return SANE_STATUS_INVAL; +	} + +	if (cs->scanning == SANE_TRUE) +	{ +		DBG(1,"sane_control_option: That scanner (%p) is scanning!\n", +				h); +		return SANE_STATUS_DEVICE_BUSY; +	} + +	switch(act)  +	{ +		case SANE_ACTION_GET_VALUE: +			switch (opt)  +			{ +				case OPT_COLOUR_MODE: +					strcpy((char *)val,  +							cmodes[cs->vals[opt]]); +					break; +				case OPT_DEPTH: +					strcpy((char *)val,  +							depths[cs->vals[opt]]); +					break; +				case OPT_RESOLUTION: +					*((int *)val) = res600[cs->vals[opt]]; +					break; +				default: +					*((int *)val) = cs->vals[opt]; +					break; +			} +			break; +		case SANE_ACTION_SET_VALUE: +			/* val has been checked for NULL if opt != OPT_CAL */ +			if (opt != OPT_CAL) i = *((int *)val); +			if (info != NULL) *info = 0; +			switch (opt) { +				case OPT_NUM_OPTIONS: +					/* you can't set that! */ +					return SANE_STATUS_INVAL; +				case OPT_RESOLUTION: +					i = cs->vals[opt]; +					cs->vals[opt] = 1; +					maxresi = cs->opt[OPT_RESOLUTION]. +						constraint.word_list[0]; + +					while ((cs->vals[opt] <= maxresi) &&  +							(res600[cs->vals[opt]] +							 < *((int *)val))) +					{ +						cs->vals[opt] += 1; +					} + +					if (res600[cs->vals[opt]] !=  +							*((int *)val)) +					{ +						if (info != NULL) *info |=  +							SANE_INFO_INEXACT; +					} +					break; +				case OPT_COLOUR_MODE: +					cs->vals[opt] = 0; +					while ((cmodes[cs->vals[opt]] != NULL) +							&& strcmp(cmodes[cs->vals[opt]],  +								(char *)val)) +					{ +						cs->vals[opt] += 1; +					} +					if (info != NULL) *info |=  +						SANE_INFO_RELOAD_PARAMS; +					break; +				case OPT_DEPTH: +					cs->vals[opt] = 0; +					while ((depths[cs->vals[opt]] != NULL) +							&& strcmp(depths[cs->vals[opt]],  +								(char *)val)) +					{ +						cs->vals[opt] += 1; +					} +					if (info != NULL) *info |=  +						SANE_INFO_RELOAD_PARAMS; +					break; +				case OPT_TL_X: +				case OPT_BR_X: +				case OPT_TL_Y: +				case OPT_BR_Y: +					if ((i<cs->opt[opt].constraint.range->min) || (i>cs->opt[opt].constraint.range->max)) +						return SANE_STATUS_INVAL; +					cs->vals[opt] = i; +					break; +				case OPT_CAL: +					/* Call the calibration code */ +					if ((cs->weights_file==NULL) || +							cs->cal_readonly +					   ) +						DBG(2, ">> calibrate(x, "  +								"NULL)\n"); +					else +						DBG(2, ">> calibrate(x," +								"%s)\n", +								cs->weights_file); + +					if (cs->cal_readonly) tmp =  +						sanei_canon_pp_calibrate( +								&(cs->params),  +								NULL); +					else tmp = sanei_canon_pp_calibrate( +							&(cs->params),  +							cs->weights_file); + +					DBG(2, "<< %d calibrate\n",  +							tmp); +					if (tmp != 0) { +						DBG(1, "sane_control_option: " +								"WARNING: " +								"calibrate " +								"returned %d!",  +								tmp); +						cs->cal_valid =  +							SANE_FALSE; +						return SANE_STATUS_IO_ERROR; +					} else { +						cs->cal_valid =  +							SANE_TRUE; +					} + +					break; +					/*case OPT_PREVIEW: +					  if (i) cs->vals[opt] = 1; +					  else cs->vals[opt] = 0; +					  break;*/ +				default: +					/* Should never happen */ +					return SANE_STATUS_INVAL; +			} +			break; +		case SANE_ACTION_SET_AUTO: +			DBG(2, "sane_control_option: attempt at " +					"automatic control! (unsupported)\n"); +			/* Auto? are they mad? I'm not that smart! */ +			/* fall through. */ +		default: +			return SANE_STATUS_INVAL; +	} + + +	DBG(2, "<< sane_control_option\n"); +	return SANE_STATUS_GOOD; +} + + +/************************************************************************* + * + * sane_get_parameters() + * + * Get information about the next packet. If a scan hasn't started, results + * only have to be best guesses. + * + *************************************************************************/ +	SANE_Status +sane_get_parameters (SANE_Handle h, SANE_Parameters *params) +{ +	int res, max_width, max_height, max_res; +        CANONP_Scanner *cs = ((CANONP_Scanner *)h); +	DBG(2, ">> sane_get_parameters (h=%p, params=%p)\n", (void*)h,  +			(void*)params); + +	if (h == NULL) return SANE_STATUS_INVAL; + +	if (cs->opened == SANE_FALSE) +	{ +		DBG(1,"sane_get_parameters: That scanner (%p) ain't " +				"open yet\n", h); +		return SANE_STATUS_INVAL; +	} + +	/* We use 600 res list here because the 300 res list is just a shorter +	 * version, so this will always work. */ +	res = res600[cs->vals[OPT_RESOLUTION]]; + +	/*  +	 * These don't change whether we're scanning or not  +	 * NOTE: Assumes options don't change after scanning commences, which +         *       is part of the standard +	 */ + +	/* Copy the options stored in the vals into the scaninfo */ +	params->pixels_per_line =  +                ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) * res) / MM_PER_IN; +	params->lines = ((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) * res)  +                / MM_PER_IN; + +	/* FIXME: Magic numbers ahead! */ + +	max_res = cs->params.scanheadwidth == 2552 ? 300 : 600; + +	/* x values have to be divisible by 4 (round down) */ +	params->pixels_per_line -= (params->pixels_per_line%4); + +        /* Can't scan less than 64 */ +        if (params->pixels_per_line < 64) params->pixels_per_line = 64; + +	max_width = cs->params.scanheadwidth / (max_res / res); + +	max_height = (cs->params.scanheadwidth == 2552 ? 3508 : 7016) /  +                                        (max_res / res); + +        if(params->pixels_per_line > max_width)  +                params->pixels_per_line = max_width; +        if(params->lines > max_height) params->lines = max_height; + + +	params->depth = cs->vals[OPT_DEPTH] ? 16 : 8; + +	switch (cs->vals[OPT_COLOUR_MODE])  +	{ +		case 0: +			params->format = SANE_FRAME_GRAY; +			break; +		case 1: +			params->format = SANE_FRAME_RGB; +			break; +		default: +			/* shouldn't happen */ +			break; +	} + + +	if (!(params->pixels_per_line)) { +		params->last_frame = SANE_TRUE; +		params->lines = 0; +	}  + +	/* Always the "last frame" */ +	params->last_frame = SANE_TRUE; + +	params->bytes_per_line = params->pixels_per_line * (params->depth/8) * +		(cs->vals[OPT_COLOUR_MODE] ? 3 : 1); + +	DBG(10, "get_params: bytes_per_line=%d, pixels_per_line=%d, lines=%d\n" +                "max_res=%d, res=%d, max_height=%d, br_y=%d, tl_y=%d, " +		"mm_per_in=%f\n", +                params->bytes_per_line, params->pixels_per_line, params->lines, +                max_res, res, max_height, cs->vals[OPT_BR_Y],  +		cs->vals[OPT_TL_Y], MM_PER_IN); + +	DBG(2, "<< sane_get_parameters\n"); +	return SANE_STATUS_GOOD; +} + + +/************************************************************************* + * + * sane_start() + * + * Starts scanning an image. + * + *************************************************************************/ +	SANE_Status +sane_start (SANE_Handle h) +{ +	unsigned int i, res, max_width, max_height, max_res, tmp; +	CANONP_Scanner *cs = ((CANONP_Scanner *)h); +	DBG(2, ">> sane_start (h=%p)\n", h); + +	if (h == NULL) return SANE_STATUS_INVAL; + +	if (cs->scanning) return SANE_STATUS_DEVICE_BUSY; +	if (cs->opened == SANE_FALSE) +	{ +		DBG(1,"sane_start: That scanner (%p) ain't " +				"open yet\n", h); +		return SANE_STATUS_INVAL; +	} + + +	/* We use 600 res list here because the 300 res list is just a shorter +	 * version, so this will always work. */ +	res = res600[cs->vals[OPT_RESOLUTION]]; + +	/* Copy the options stored in the vals into the scaninfo */ +	cs->scan.width = ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) * res)  +                / MM_PER_IN; +	cs->scan.height = ((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) * res)  +                / MM_PER_IN; + +	cs->scan.xoffset = (cs->vals[OPT_TL_X] * res) / MM_PER_IN; +	cs->scan.yoffset = (cs->vals[OPT_TL_Y] * res) / MM_PER_IN; + +        /*  +         * These values have to pass the requirements of not exceeding  +         * dimensions (simple clipping) and both width values have to be some  +         * integer multiple of 4  +         */ + +	/* FIXME: Magic numbers ahead! */ + +	max_res = cs->params.scanheadwidth == 2552 ? 300 : 600; + +	/* x values have to be divisible by 4 (round down) */ +	cs->scan.width -= (cs->scan.width%4); +	cs->scan.xoffset -= (cs->scan.xoffset%4); + +	/* Can't scan less than 64 */ +        if (cs->scan.width < 64) cs->scan.width = 64; + +	max_width = cs->params.scanheadwidth / (max_res / res); + +	max_height = (cs->params.scanheadwidth == 2552 ? 3508 : 7016) /  +                                        (max_res / res); + +        if (cs->scan.width > max_width) cs->scan.width = max_width; +	if (cs->scan.width + cs->scan.xoffset > max_width) cs->scan.xoffset =  +		max_width - cs->scan.width; +        if (cs->scan.height > max_height) cs->scan.height = max_height; + +	/* We pass a value to init_scan which is the power of 2 that 75 +	 * is multiplied by for the resolution.  ie: +	 * 75 -> 0 +	 * 150 -> 1 +	 * 300 -> 2 +	 * 600 -> 4 +	 * +	 * This rather strange parameter is a result of the way the scanner +	 * takes its resolution argument +	 */ + +	i = 0; +	while (res > 75) +	{ +		i++; +		res = res >> 1; +	} + +	/* FIXME? xres == yres for now. */ +	cs->scan.xresolution = i; +	cs->scan.yresolution = i; + +	if (((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) <= 0) ||  +			((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) <= 0)) +	{ +		DBG(1,"sane_start: height = %d, Width = %d. " +				"Can't scan void range!", +				cs->scan.height, cs->scan.width); +		return SANE_STATUS_INVAL; +	} + +	cs->scan.mode = cs->vals[OPT_COLOUR_MODE]; + +	DBG(10, ">> init_scan()\n"); +	tmp = sanei_canon_pp_init_scan(&(cs->params), &(cs->scan)); +	DBG(10, "<< %d init_scan\n", tmp); + +	if (tmp != 0) { +		DBG(1,"sane_start: WARNING: init_scan returned %d!", tmp); +		return SANE_STATUS_IO_ERROR; +	} +	cs->scanning = SANE_TRUE; +	cs->cancelled = SANE_FALSE; +	cs->sent_eof = SANE_FALSE; +	cs->lines_scanned = 0; +	cs->bytes_sent = 0; + +	DBG(2, "<< sane_start\n"); + +	return SANE_STATUS_GOOD; +} + + +/************************************************************************* + * + * sane_read() + * + * Reads some information from the buffer. + * + *************************************************************************/ +	SANE_Status +sane_read (SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp) +{ +	CANONP_Scanner *cs = ((CANONP_Scanner *)h); +	image_segment *is; +	unsigned int lines, bytes, bpl; +	unsigned int i; +	short *shortptr; +	SANE_Byte *charptr; +	int tmp; + +	static SANE_Byte *lbuf; +	static unsigned int bytesleft; + +	DBG(2, ">> sane_read (h=%p, buf=%p, maxlen=%d)\n", h,  +			(const void *)buf, maxlen); + +	/* default to returning 0 - for errors */ +	*lenp = 0; + +	if ((h == NULL) || (buf == NULL) || (lenp == NULL))  +	{ +		DBG(1, "sane_read: This frontend's passing me dodgy gear! " +				"(h=%p, buf=%p, lenp=%p)\n",  +				(void*)h, (void*)buf, (void*)lenp); +		return SANE_STATUS_INVAL; +	} + +	/* Now we have to see if we have some leftover from last time */ + +	if (read_leftover != NULL)  +	{ +		/* feed some more data in until we've run out - don't care  +		 * whether or not we _think_ the scanner is scanning now,  +		 * because we may still have data left over to send */ +		DBG(200, "sane_read: didn't send it all last time\n"); + +		/* Now feed it some data from lbuf */ +		if (bytesleft <= (unsigned int)maxlen) +		{ +			/* enough buffer to send the lot */ +			memcpy(buf, read_leftover, bytesleft); +			free(lbuf); +			*lenp = bytesleft; +			lbuf = NULL; +			read_leftover = NULL; +			bytesleft = 0; +                        cs->bytes_sent += bytesleft; +			return SANE_STATUS_GOOD; + +		} else { +			/* only enough to send maxlen */ +			memcpy(buf, read_leftover, maxlen); +			read_leftover += maxlen; +			bytesleft -= maxlen; +			*lenp = maxlen; +                        cs->bytes_sent += maxlen; +		        DBG(100, "sane_read: sent %d bytes, still have %d to " +                                "go\n", maxlen, bytesleft); +			return SANE_STATUS_GOOD; +		} + +	}  + + +	/* Has the last scan ended (other than by cancelling)? */ +	if (((unsigned)cs->scan.height <= (unsigned)cs->lines_scanned)  +	    || (cs->sent_eof) || !(cs->scanning)) +	{ +		cs->sent_eof = SANE_TRUE; +		cs->scanning = SANE_FALSE; +		cs->cancelled = SANE_FALSE; +		cs->lines_scanned = 0; +		cs->bytes_sent = 0; +		read_leftover = NULL; +		return SANE_STATUS_EOF; +	} + +	/* At this point we have to read more data from the scanner - or the  +	 * scan has been cancelled, which means we have to call read_segment +	 * to leave the scanner consistant */ + +	/* Decide how many lines we can fit into this buffer */ +	if (cs->vals[OPT_DEPTH] == 0) +		bpl = cs->scan.width * (cs->vals[OPT_COLOUR_MODE] ? 3 : 1); +	else +		bpl = cs->scan.width * (cs->vals[OPT_COLOUR_MODE] ? 6 : 2); + +        /* New way: scan a whole scanner buffer full, and return as much as  +         * the frontend wants.  It's faster and more reliable since the  +         * scanners crack the shits if we ask for too many small packets */ +	lines = (BUF_MAX * 4 / 5) / bpl; + +	if (lines > (cs->scan.height - cs->lines_scanned))  +		lines = cs->scan.height - cs->lines_scanned; + +	if (!lines) +	{ +		/* can't fit a whole line into the buffer  +                 * (should never happen!) */ +		lines = 1; +	} + +	bytes = lines * bpl; + +	/* Allocate a local buffer to hold the data while we play */ +	if ((lbuf = malloc(bytes)) == NULL) +	{ +		DBG(10, "sane_read: Not enough memory to hold a " +				"local buffer.  You're doomed\n"); +		return SANE_STATUS_NO_MEM; +	} + + +	/* This call required a lot of debugging information.. */ +	DBG(10, "sane_read: Here's what we're sending read_segment:\n"); +	DBG(10, "scanner setup: shw=%d xres=%d yres=%d %d %d id=%s\n", +			cs->params.scanheadwidth, +			cs->params.natural_xresolution, +			cs->params.natural_yresolution, +			cs->params.max_xresolution, +			cs->params.max_yresolution, +			(cs->params.id_string)+8); +	DBG(10, "scan_params->: width=%d, height=%d, xoffset=%d, " +			"yoffset=%d\n\txresolution=%d, yresolution=%d, " +			"mode=%d, (lines=%d)\n", +			cs->scan.width, cs->scan.height,  +			cs->scan.xoffset, cs->scan.yoffset, +			cs->scan.xresolution, cs->scan.yresolution, +			cs->scan.mode, lines); + +	DBG(2, ">> read_segment(x, x, x, %d, %d, %d)\n", +			lines, cs->cal_valid,  +			cs->scan.height - cs->lines_scanned); +	tmp = sanei_canon_pp_read_segment(&is, &(cs->params), &(cs->scan),  +			lines, cs->cal_valid,  +			cs->scan.height - cs->lines_scanned); +	DBG(2, "<< %d read_segment\n", tmp); + +	if (tmp != 0) { +		if (cs->cancelled) +		{ +			DBG(10, "sane_read: cancelling.\n"); +			cs->sent_eof = SANE_TRUE; +			cs->scanning = SANE_FALSE; +			read_leftover = NULL; +			sanei_canon_pp_abort_scan(&(cs->params)); +			return SANE_STATUS_CANCELLED; +		} +		DBG(1, "sane_read: WARNING: read_segment returned %d!\n", tmp); +		return SANE_STATUS_IO_ERROR; +	} + +	DBG(10, "sane_read: bpl=%d, lines=%d, bytes=%d\n", bpl, lines, bytes); + +	cs->lines_scanned += lines; + +	/* translate data out of buffer */ +	if (cs->vals[OPT_DEPTH] == 0) +	{ +		/* 8bpp */ +		for(i = 0; i < bytes; i++) +		{ +			charptr = lbuf + i; +			if (cs->vals[OPT_COLOUR_MODE]) +			{ +				if (i % 3 == 0) charptr += 2; +				if (i % 3 == 2) charptr -= 2; +			} +			*charptr = *((char *)(is->image_data) + (i*2)); +		} +	} +	else +	{ +		/* 16bpp */ +		for(i = 0; i < (bytes/2); i++) +		{ +			shortptr = ((short *)lbuf + i); +			if (cs->vals[OPT_COLOUR_MODE]) +			{ +				if (i % 3 == 0) shortptr += 2; +				if (i % 3 == 2) shortptr -= 2; +			} +			*shortptr = MAKE_SHORT( +					*((char *)(is->image_data) + (i*2)), +					*((char *)(is->image_data) + (i*2)+1) +					); +		} +	} + +	/* Free data structures allocated in read_segment */ +	free(is->image_data); +	free(is); + +	/* Now feed it some data from lbuf */ +	if (bytes <= (unsigned int)maxlen) +	{ +		/* enough buffer to send the lot */ +		memcpy(buf, lbuf, bytes); +		*lenp = bytes; +		free(lbuf); +		lbuf = NULL; +		read_leftover = NULL; +		bytesleft = 0; +                cs->bytes_sent += bytes; + +	} else { +		/* only enough to send maxlen */ +		memcpy(buf, lbuf, maxlen); +		*lenp = maxlen; +		read_leftover = lbuf + maxlen; +		bytesleft = bytes - maxlen; +                cs->bytes_sent += maxlen; +		DBG(100, "sane_read: sent %d bytes, still have %d to go\n", +                        maxlen, bytesleft); +	} + +	if ((unsigned)cs->lines_scanned >= cs->scan.height) +	{ +		/* The scan is over! Don't need to call anything in the  +		 * hardware, it will sort itself out */ +		DBG(10, "sane_read: Scan is finished.\n"); +		cs->scanning = SANE_FALSE; +		cs->lines_scanned = 0; +		cs->bytes_sent = 0; +	} + +	DBG(2, "<< sane_read\n"); +	return SANE_STATUS_GOOD; +} + + +/************************************************************************* + * + * sane_cancel() + * + * Cancels a scan in progress + * + *************************************************************************/ +	void +sane_cancel (SANE_Handle h) +{ +	/* Note: assume handle is valid apart from NULLs */ +	CANONP_Scanner *cs = ((CANONP_Scanner *)h); + +	DBG(2, ">> sane_cancel (h=%p)\n", h); +	if (h == NULL) return; + +	read_leftover = NULL; + +	if (!(cs->scanning))  +	{ +		DBG(2, "<< sane_cancel (not scanning)\n"); +		return; +	} + +	cs->cancelled = SANE_TRUE; +	cs->params.abort_now = 1; + +	DBG(2, "<< sane_cancel\n"); +} + + +/************************************************************************* + * + * sane_close() + * + * Closes a scanner handle.  Scanner is assumed to be free after this. + * + *************************************************************************/ +	void +sane_close (SANE_Handle h) +{ +	/* Note: assume handle is valid apart from NULLs */ +	CANONP_Scanner *cs = ((CANONP_Scanner *)h); +	DBG(2, ">> sane_close (h=%p)\n", h); +	if (h == NULL) return; + +	if (cs->opened == SANE_FALSE) +	{ +		DBG(1,"sane_close: That scanner (%p) ain't " +				"open yet\n", h); +		return; +	} + +	/* Put scanner back in transparent mode */ +	sanei_canon_pp_close_scanner(&(cs->params)); + +	cs->opened = SANE_FALSE; +	 +	/* if it was scanning, it's not any more */ +	cs->scanning = SANE_FALSE; +	cs->sent_eof = SANE_TRUE; + +	ieee1284_release(cs->params.port); + +	DBG(2, "<< sane_close\n"); +} + + +/************************************************************************* + * + * sane_exit() + * + * Shut it down! + * + *************************************************************************/ +	void +sane_exit (void) +{ +	CANONP_Scanner *dev, *next; + +	DBG(2, ">> sane_exit\n"); + +	for (dev = first_dev; dev != NULL; dev = next) +	{ +		next = dev->next; + +		/* These were only created if the scanner has been init'd */ +		 +		/* Should normally nullify pointers after freeing, but in +		 * this case we're about to free the whole structure so  +		 * theres not a lot of point. */ + +		/* Constraints (mostly) allocated when the scanner is opened */ +		if(dev->opt[OPT_TL_X].constraint.range) +			free((void *)(dev->opt[OPT_TL_X].constraint.range)); +		if(dev->opt[OPT_TL_Y].constraint.range) +			free((void *)(dev->opt[OPT_TL_Y].constraint.range)); +		if(dev->opt[OPT_BR_X].constraint.range) +			free((void *)(dev->opt[OPT_BR_X].constraint.range)); +		if(dev->opt[OPT_BR_Y].constraint.range) +			free((void *)(dev->opt[OPT_BR_Y].constraint.range)); + +		/* Weights file now on a per-scanner basis */ +		if (dev->weights_file != NULL) +			free(dev->weights_file); + +		if (dev->scanner_present) +		{ +			if (dev->opened == SANE_TRUE) +			{ +				/* naughty boys, should have closed first */ +				ieee1284_release(dev->params.port); +			} +			ieee1284_close(dev->params.port); +		} + +		free (dev); +	} + +	first_dev = NULL; +	def_scanner = NULL; +	read_leftover = NULL; +	num_devices = 0; + +	/* FIXEDME: this created a segfault in DLL code. */ +	/* Bug was fixed in libieee1284 0.1.5 */ +	ieee1284_free_ports(&pl); + +	DBG(2, "<< sane_exit\n"); +} + + +/************************************************************************* + * + * init_device() + * + * (Not part of the SANE API) + * + * Initialises a CANONP_Scanner data structure for a new device. + * NOTE: The device is not ready to scan until initialise() has been  + * called in scan library! + * + *************************************************************************/ +static SANE_Status init_device(struct parport *pp)  +{ +	int i; +	static const char *hw_vendor = "CANON"; +	static const char *hw_type = "flatbed scanner"; +	static const char *opt_names[] = { +		SANE_NAME_NUM_OPTIONS,  +		SANE_NAME_SCAN_RESOLUTION, +		SANE_NAME_SCAN_MODE, +		SANE_NAME_BIT_DEPTH, +		SANE_NAME_SCAN_TL_X, +		SANE_NAME_SCAN_TL_Y, +		SANE_NAME_SCAN_BR_X, +		SANE_NAME_SCAN_BR_Y, +		SANE_NAME_QUALITY_CAL +#if 0 +		SANE_NAME_GAMMA_R, +		SANE_NAME_GAMMA_G, +		SANE_NAME_GAMMA_B +#endif +	}; +	static const char *opt_titles[] = { +		SANE_TITLE_NUM_OPTIONS,  +		SANE_TITLE_SCAN_RESOLUTION, +		SANE_TITLE_SCAN_MODE, +		SANE_TITLE_BIT_DEPTH, +		SANE_TITLE_SCAN_TL_X, +		SANE_TITLE_SCAN_TL_Y, +		SANE_TITLE_SCAN_BR_X, +		SANE_TITLE_SCAN_BR_Y, +		SANE_TITLE_QUALITY_CAL +#if 0 +		SANE_TITLE_GAMMA_R, +		SANE_TITLE_GAMMA_G, +		SANE_TITLE_GAMMA_B +#endif +	}; +	static const char *opt_descs[] = { +		SANE_DESC_NUM_OPTIONS,  +		SANE_DESC_SCAN_RESOLUTION, +		SANE_DESC_SCAN_MODE, +		SANE_DESC_BIT_DEPTH, +		SANE_DESC_SCAN_TL_X, +		SANE_DESC_SCAN_TL_Y, +		SANE_DESC_SCAN_BR_X, +		SANE_DESC_SCAN_BR_Y, +		SANE_DESC_QUALITY_CAL +#if 0 +		SANE_DESC_GAMMA_R, +		SANE_DESC_GAMMA_G, +		SANE_DESC_GAMMA_B +#endif +	}; + +	CANONP_Scanner *cs = NULL; + +	DBG(2, ">> init_device\n"); + +	cs = malloc(sizeof(*cs)); +	if (cs == NULL) +	{ +		return SANE_STATUS_NO_MEM; +	} +	memset(cs, 0, sizeof(*cs)); + +#if 0 +	if ((cs->params.port = malloc(sizeof(*(cs->params.port)))) == NULL)  +		return SANE_STATUS_NO_MEM; + +	memcpy(cs->params.port, pp, sizeof(*pp)); +#endif + +	cs->params.port = pp; + +	/* ensure these are null to start off with, otherwise they might be +	 * erroneously free'd.  Note that we set everything to 0 above +	 * but that's not *always* the same thing */ +	cs->params.blackweight = NULL; +	cs->params.redweight = NULL; +	cs->params.greenweight = NULL; +	cs->params.blueweight = NULL; + +	/* Set some sensible defaults */ +	cs->hw.name = cs->params.port->name; +	cs->hw.vendor = hw_vendor; +	cs->hw.type = hw_type; +	cs->opened = SANE_FALSE; +	cs->scanning = SANE_FALSE; +	cs->cancelled = SANE_FALSE; +	cs->sent_eof = SANE_TRUE; +	cs->lines_scanned = 0; +	cs->bytes_sent = 0; +	cs->init_mode = INITMODE_AUTO; + +	DBG(10, "init_device: [configuring options]\n"); + +	/* take a punt at each option, then we change it later */ +	for (i = 0; i < NUM_OPTIONS; i++) +	{ +		cs->opt[i].name = opt_names[i]; +		cs->opt[i].title = opt_titles[i]; +		cs->opt[i].desc = opt_descs[i]; +		cs->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; +		cs->opt[i].type = SANE_TYPE_INT; +		cs->opt[i].size = sizeof(SANE_Int); +	} + +	DBG(100, "init_device: configuring opt: num_options\n"); +	/* The number of options option */ + +	cs->opt[OPT_NUM_OPTIONS].unit = SANE_UNIT_NONE; +	cs->opt[OPT_NUM_OPTIONS].cap = SANE_CAP_SOFT_DETECT; +	cs->vals[OPT_NUM_OPTIONS] = NUM_OPTIONS; + +	DBG(100, "init_device: configuring opt: resolution\n"); + +	/* The resolution of scanning (X res == Y res for now)*/ +	cs->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; +	cs->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; +	/* should never point at first element (wordlist size) */ +	cs->vals[OPT_RESOLUTION] = 1;  + +	DBG(100, "init_device: configuring opt: colour mode\n"); + +	/* The colour mode (0=grey 1=rgb) */ +	cs->opt[OPT_COLOUR_MODE].type = SANE_TYPE_STRING; +	cs->opt[OPT_COLOUR_MODE].size = 20; +	cs->opt[OPT_COLOUR_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; +	/* Set this one here because it doesn't change by scanner (yet) */ +	cs->opt[OPT_COLOUR_MODE].constraint.string_list = cmodes; + +	DBG(100, "init_device: configuring opt: bit depth\n"); + +	/* The bit depth */ +	cs->opt[OPT_DEPTH].type = SANE_TYPE_STRING; +	cs->opt[OPT_DEPTH].size = 20; +	cs->opt[OPT_DEPTH].cap |= SANE_CAP_EMULATED; +	cs->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST; +	cs->opt[OPT_DEPTH].constraint.string_list = depths; + +	DBG(100, "init_device: configuring opt: tl-x\n"); + +	/* The top-left-x */ +	cs->opt[OPT_TL_X].unit = SANE_UNIT_MM; +	cs->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + +	DBG(100, "init_device: configuring opt: tl-y\n"); + +	/* The top-left-y */ +	cs->opt[OPT_TL_Y].unit = SANE_UNIT_MM; +	cs->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + +	DBG(100, "init_device: configuring opt: br-x\n"); + +	/* The bottom-right-x */ +	cs->opt[OPT_BR_X].unit = SANE_UNIT_MM; +	cs->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; +	/* default scan width */ +	cs->vals[OPT_BR_X] = 100; + +	DBG(100, "init_device: configuring opt: br-y\n"); + +	/* The bottom-right-y */ +	cs->opt[OPT_BR_Y].unit = SANE_UNIT_MM; +	cs->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; +	cs->vals[OPT_BR_Y] = 100; + +	DBG(100, "init_device: configuring opt: calibrate\n"); + +	/* The calibration button */ +	cs->opt[OPT_CAL].type = SANE_TYPE_BUTTON; +	cs->opt[OPT_CAL].constraint_type = SANE_CONSTRAINT_NONE; +	if (cs->cal_readonly)  +		cs->opt[OPT_CAL].cap |= SANE_CAP_INACTIVE;  + +#if 0 +	/* the gamma values (once we do them) */ +	cs->opt[OPT_GAMMA_R].caps |= SANE_CAP_ADVANCED; +	cs->opt[OPT_GAMMA_G].caps |= SANE_CAP_ADVANCED; +	cs->opt[OPT_GAMMA_B].caps |= SANE_CAP_ADVANCED; +#endif + +	/* +	 * NOTE: Ranges and lists are actually set when scanner is opened,  +	 * becase that's when we find out what sort of scanner it is  +	 */ + +	DBG(100, "init_device: done opts\n"); + +	/* add it to the head of the tree */ +	cs->next = first_dev; +	first_dev = cs; + +	num_devices++; + +	DBG(2, "<< init_device\n"); + +	return SANE_STATUS_GOOD; +} + + +/************************************************************************* + * + * These two are optional ones... maybe if I get really keen?  + * + *************************************************************************/ +	SANE_Status +sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking) +{ +	DBG(2, ">> sane_set_io_mode (%p, %d) (not really supported)\n", +			h, non_blocking); + +	if (non_blocking == SANE_FALSE) +		return SANE_STATUS_GOOD; + +	DBG(2, "<< sane_set_io_mode\n"); +	return SANE_STATUS_UNSUPPORTED; +} + +	SANE_Status +sane_get_select_fd (SANE_Handle h, SANE_Int *fdp) +{ +	DBG(2, ">> sane_get_select_fd (%p, %p) (not supported)\n", h,  +			(const void *)fdp); +	DBG(2, "<< sane_get_select_fd\n"); +	return SANE_STATUS_UNSUPPORTED; +} + + +/************************************************************************* + * + * init_cal(): Try to create a calibration file + * has to be changed. + * + ************************************************************************/ +static int init_cal(char *file) +{ +	char *tmp, *path; +	int f, i; + +	if ((f = open(file, O_CREAT | O_WRONLY, 0600)) < 0) +	{ +		if (errno == ENOENT) +		{ +			/* we need to try and make ~/.sane perhaps - +			 * find the last / in the file path, and try  +			 * to create it */ +			if ((tmp = strrchr(file, '/')) == NULL) +				return -1; +			path = strdup(file); +			*(path + (tmp-file)) = '\0'; +			i = mkdir(path, 0777); +			free(path); +			if (i) return -1; +			/* Path has been created, now try this again.. */ +			if ((f = open(file, O_CREAT | O_WRONLY, 0600)) < 0) +				return -1; +		} +		else  +		{ +			/* Error is something like access denied - too  +			 * hard to fix, so i give up... */ +			return -1; +		} +	} +	/* should probably set defaults here.. */ +	close(f); +	return 0; +} + +/************************************************************************* + * + * fix_weights_file(): Ensures that the weights_file setting for a given  + * scanner is valid + * + ************************************************************************/ +static SANE_Status fix_weights_file(CANONP_Scanner *cs) +{ +	char *tmp, *myhome, buf[PATH_MAX]; +	int i; +	struct stat *f_stat; + + +	if (cs == NULL) +	{ +		DBG(0, "fix_weights_file: FATAL: NULL passed by my code, " +				"please report this!\n"); +		return SANE_STATUS_INVAL; +	} + +	/* Assume this is false and then correct it */ +	cs->cal_readonly = SANE_FALSE; + +	if (cs->weights_file == NULL) +	{ +		/* Will be of form canon_pp-calibration-parport0 or -0x378 */ +		sprintf(buf, "~/.sane/canon_pp-calibration-%s", +				cs->params.port->name); +		cs->weights_file = strdup(buf); +	} + +	/* Get the user's home dir if they used ~ */ +	if (cs->weights_file[0] == '~') +	{ +		if ((tmp = malloc(PATH_MAX)) == NULL)  +			return SANE_STATUS_NO_MEM; +		if ((myhome = getenv("HOME")) == NULL) +		{ +			DBG(0,"fix_weights_file: FATAL: ~ used, but $HOME not" +					" set!\n"); +			free(tmp); +			tmp = NULL; +			return SANE_STATUS_INVAL; +		} +		strncpy(tmp, myhome, PATH_MAX); +		strncpy(tmp+strlen(tmp), (cs->weights_file)+1,  +				PATH_MAX-strlen(tmp)); + +		free(cs->weights_file); +		cs->weights_file = tmp; +	} + +	if ((f_stat = malloc(sizeof(*f_stat))) == NULL) +		return SANE_STATUS_NO_MEM; + +	if(stat(cs->weights_file, f_stat)) +	{ +		/* this non-intuitive if basically is if we got some error that +		 * wasn't no-such-file, or we can't create the file.. */ +		if ((errno != ENOENT) || init_cal(cs->weights_file)) +		{ +			/* Some nasty error returned. Give up. */ +			DBG(2,"fix_weights_file: error stating cal file" +					" (%s)\n", strerror(errno)); +			DBG(2,"fix_weights_file: Changes to cal data won't" +					" be saved!\n"); +			free(cs->weights_file); +			cs->weights_file = NULL; +		} +	} +	else +	{ + +		/* No error returned.. Check read/writability */ +		i = open(cs->weights_file, O_RDWR | O_APPEND); +		if (i <= 0) +		{ +			DBG(10,"fix_weighs_file: Note: Changes to cal data " +					"won't be saved!\n"); +			i = open(cs->weights_file, O_RDONLY); +			if (i <= 0) +			{ +				/*  +				 * Open failed (do i care why?)  +				 */ +				DBG(2,"fix_weights_file: error opening cal " +						"(%s)\n", strerror(errno)); +				free(cs->weights_file); +				cs->weights_file = NULL; +			} +			else +			{ +				DBG(2,"fix_weights_file: file is read-only, " +						"changes won't be saved\n"); +				cs->cal_readonly = SANE_TRUE; +				close(i); +			} +		} +		else +		{ +			/* good! */ +			DBG(10,"fix_weights_file: Calibration file is good " +					"for opening!\n"); +			close(i); +		} +	} + +	/* cleanup */ +	free(f_stat); + +	return SANE_STATUS_GOOD; +} + +/* detect_mode + * PRE: + *	cs->params.port is not open + * POST: + * 	cs->params.port is left opened iff SANE_STATUS_GOOD returned.  + */ + +SANE_Status detect_mode(CANONP_Scanner *cs)  +{ + +	int capabilities, tmp; + +	/* Open then claim parallel port using libieee1284 */ +	DBG(10,"detect_mode: Opening port %s\n", (cs->params.port->name)); + +	tmp = ieee1284_open(cs->params.port, 0, &capabilities); + +	if (tmp != E1284_OK) +	{ +		switch (tmp) +		{ +			case E1284_INVALIDPORT: +				DBG(1, "detect_mode: Invalid port.\n"); +				break; +			case E1284_SYS: +				DBG(1, "detect_mode: System error: %s\n",  +						strerror(errno)); +				break; +			case E1284_INIT: +				DBG(1, "detect_mode: Initialisation error.\n"); +				break; +			default: +				DBG(1, "detect_mode: Unknown error.\n"); +				break; +		} +		return SANE_STATUS_IO_ERROR; +	} + +	DBG(10,"detect_mode: Claiming port.\n"); + +	if (ieee1284_claim(cs->params.port) != E1284_OK) +	{ +		DBG(1,"detect_mode: Unable to claim port\n"); +		ieee1284_close(cs->params.port); +		return SANE_STATUS_IO_ERROR; +	} + + +	/* Check that compatibility-mode (required) is supported */ +	if (!(capabilities & CAP1284_COMPAT)) +	{ +		DBG(0,"detect_mode: Compatibility mode (required) not " +				"supported.\n"); +		ieee1284_release(cs->params.port); +		ieee1284_close(cs->params.port); +		return SANE_STATUS_IO_ERROR; +	} + +	/* Check capabilities which will enchance speed */ +	if (capabilities & CAP1284_ECP) +		DBG(2, "detect_mode: Port supports ECP-H.\n"); +	else if (capabilities & CAP1284_ECPSWE) +		DBG(2, "detect_mode: Port supports ECP-S.\n"); +	if (capabilities & CAP1284_IRQ) +		DBG(2, "detect_mode: Port supports interrupts.\n"); +	if (capabilities & CAP1284_DMA) +		DBG(2, "detect_mode: Port supports DMA.\n"); + +	/* Check whether ECP mode is possible */ +	if (capabilities & CAP1284_ECP) +	{ +		cs->ieee1284_mode = M1284_ECP; +		DBG(10, "detect_mode: Using ECP-H Mode\n"); +	}	 +	else if (capabilities & CAP1284_ECPSWE) +	{ +		cs->ieee1284_mode = M1284_ECPSWE; +		DBG(10, "detect_mode: Using ECP-S Mode\n"); +	} +	else if (capabilities & CAP1284_NIBBLE) +	{ +		cs->ieee1284_mode = M1284_NIBBLE; +		DBG(10, "detect_mode: Using nibble mode\n"); +	} +	else +	{ +		DBG(0, "detect_mode: No supported parport modes available!\n"); +		ieee1284_release(cs->params.port); +		ieee1284_close(cs->params.port); +		return SANE_STATUS_IO_ERROR; +	} + +	/* Check to make sure ECP mode really is supported */ +	/* Have disabled the hardware ECP check because it's always supported  +	 * by libieee1284 now, and it's too prone to hitting a ppdev bug  +	 */ + +	/* Disabled check entirely.. check now in initialise when we  +	 * actually do a read */ +#if 0 +	if ((cs->ieee1284_mode == M1284_ECP) || +			(cs->ieee1284_mode == M1284_ECPSWE)) +	{ +		DBG(1, "detect_mode: attempting a 0 byte read, if we hang " +				"here, it's a ppdev bug!\n"); +		/*  +		 * 29/06/02  +		 * NOTE: +		 * This causes an infinite loop in ppdev on 2.4.18.   +		 * Not checking on hardware ECP mode should work-around  +		 * effectively. +		 * +		 * I have sent email to twaugh about it, should be fixed in  +		 * 2.4.19 and above. +		 */ +		if (ieee1284_ecp_read_data(cs->params.port, 0, NULL, 0) ==  +				E1284_NOTIMPL) +		{ +			DBG(10, "detect_mode: Your version of libieee1284 " +					"doesn't support ECP mode - defaulting" +					" to nibble mode instead.\n"); +			cs->ieee1284_mode = M1284_NIBBLE; +		} +	} +#endif + +	if (force_nibble == SANE_TRUE) { +		DBG(10, "detect_mode: Nibble mode force in effect.\n"); +		cs->ieee1284_mode = M1284_NIBBLE; +	} + +	ieee1284_release(cs->params.port); + +	sanei_canon_pp_set_ieee1284_mode(cs->ieee1284_mode); + +	return SANE_STATUS_GOOD; +} | 
