summaryrefslogtreecommitdiff
path: root/backend/canon_pp.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/canon_pp.c')
-rw-r--r--backend/canon_pp.c2039
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;
+}