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; +} |