diff options
Diffstat (limited to 'backend/sceptre.c')
-rw-r--r-- | backend/sceptre.c | 2094 |
1 files changed, 2094 insertions, 0 deletions
diff --git a/backend/sceptre.c b/backend/sceptre.c new file mode 100644 index 0000000..fa0da20 --- /dev/null +++ b/backend/sceptre.c @@ -0,0 +1,2094 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2002 Frank Zago (sane at zago dot net) + Copyright (C) 2002 Other SANE contributors + + 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. +*/ + +/* + $Id$ + Sceptre S1200 SCSI scanner (sometimes also called S120) +*/ + +/*--------------------------------------------------------------------------*/ + +#define BUILD 10 /* 2002-03-21 */ +#define BACKEND_NAME sceptre +#define SCEPTRE_CONFIG_FILE "sceptre.conf" + +/*--------------------------------------------------------------------------*/ + + +#include "../include/sane/config.h" + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_scsi.h" +#include "../include/sane/sanei_debug.h" +#include "../include/sane/sanei_backend.h" +#include "../include/sane/sanei_config.h" +#include "../include/lassert.h" + +#include "sceptre.h" + +/*--------------------------------------------------------------------------*/ + +static const SANE_String scan_mode_list[] = { LINEART_STR, HALFTONE_STR, + GRAY_STR, COLOR_STR, NULL +}; + +static const SANE_Range gamma_range = { + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range threshold_range = { + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range halftone_range = { + 1, /* minimum */ + 4, /* maximum */ + 0 /* quantization */ +}; + +/*--------------------------------------------------------------------------*/ + +#define NUM_OF_RES 15 +/* Table of supported resolution and number of lines of color shifting. */ +static const SANE_Word resolutions_list[NUM_OF_RES + 1] = { + NUM_OF_RES, 10, 25, 30, 45, 75, 90, 150, 300, 450, 600, 750, 900, 1050, + 1125, 1200 +}; + +static const SANE_Word color_shift_list[NUM_OF_RES + 1] = { + NUM_OF_RES, 0, 0, 0, 0, 1, 1, 2, 4, 6, 8, 10, 12, 14, 15, 16 +}; + +/*--------------------------------------------------------------------------*/ + +/* Define the supported scanners and their characteristics. */ +static const struct scanners_supported scanners[] = { + /* { 6, "KINPO ", "Vividscan S600 ", "KINPO", "S600" }, */ + {6, "KINPO ", "Vividscan S120 ", "Sceptre", "S1200"} +}; + +/*--------------------------------------------------------------------------*/ + +/* List of scanner attached. */ +static Sceptre_Scanner *first_dev = NULL; +static int num_devices = 0; +static const SANE_Device **devlist = NULL; + + +/* Local functions. */ + +/* Display a buffer in the log. */ +static void +hexdump (int level, const char *comment, unsigned char *p, int l) +{ + int i; + char line[128]; + char *ptr; + + DBG (level, "%s\n", comment); + ptr = line; + for (i = 0; i < l; i++, p++) + { + if ((i % 16) == 0) + { + if (ptr != line) + { + *ptr = '\0'; + DBG (level, "%s\n", line); + ptr = line; + } + sprintf (ptr, "%3.3d:", i); + ptr += 4; + } + sprintf (ptr, " %2.2x", *p); + ptr += 3; + } + *ptr = '\0'; + DBG (level, "%s\n", line); +} + +/* Initialize a scanner entry. Return an allocated scanner with some + * preset values. */ +static Sceptre_Scanner * +sceptre_init (void) +{ + Sceptre_Scanner *dev; + + DBG (DBG_proc, "sceptre_init: enter\n"); + + /* Allocate a new scanner entry. */ + dev = malloc (sizeof (Sceptre_Scanner)); + if (dev == NULL) + { + return NULL; + } + + memset (dev, 0, sizeof (Sceptre_Scanner)); + + /* Allocate the buffer used to transfer the SCSI data. */ + dev->buffer_size = 64 * 1024; + dev->buffer = malloc (dev->buffer_size); + if (dev->buffer == NULL) + { + free (dev); + return NULL; + } + + dev->sfd = -1; + + DBG (DBG_proc, "sceptre_init: exit\n"); + + return (dev); +} + +/* Closes an open scanner. */ +static void +sceptre_close (Sceptre_Scanner * dev) +{ + DBG (DBG_proc, "sceptre_close: enter\n"); + + if (dev->sfd != -1) + { + sanei_scsi_close (dev->sfd); + dev->sfd = -1; + } + + DBG (DBG_proc, "sceptre_close: exit\n"); +} + +/* Frees the memory used by a scanner. */ +static void +sceptre_free (Sceptre_Scanner * dev) +{ + int i; + + DBG (DBG_proc, "sceptre_free: enter\n"); + + if (dev == NULL) + return; + + sceptre_close (dev); + if (dev->devicename) + { + free (dev->devicename); + } + if (dev->buffer) + { + free (dev->buffer); + } + if (dev->image) + { + free (dev->image); + } + for (i = 1; i < OPT_NUM_OPTIONS; i++) + { + if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s) + { + free (dev->val[i].s); + } + } + + free (dev); + + DBG (DBG_proc, "sceptre_free: exit\n"); +} + +/* Inquiry a device and returns TRUE if is supported. */ +static int +sceptre_identify_scanner (Sceptre_Scanner * dev) +{ + CDB cdb; + SANE_Status status; + size_t size; + int i; + + DBG (DBG_proc, "sceptre_identify_scanner: enter\n"); + + size = 36; + MKSCSI_INQUIRY (cdb, size); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status) + { + DBG (DBG_error, + "sceptre_identify_scanner: inquiry failed with status %s\n", + sane_strstatus (status)); + return (SANE_FALSE); + } + + if (size < 36) + { + DBG (DBG_error, + "sceptre_identify_scanner: not enough data to identify device\n"); + return (SANE_FALSE); + } + + dev->scsi_type = dev->buffer[0] & 0x1f; + memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08); + dev->scsi_vendor[0x08] = 0; + memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010); + dev->scsi_product[0x10] = 0; + memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04); + dev->scsi_version[0x04] = 0; + + DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\"\n", + dev->scsi_vendor, dev->scsi_product, dev->scsi_version); + + /* Lookup through the supported scanners table to find if this + * backend supports that one. */ + for (i = 0; i < NELEMS (scanners); i++) + { + if (dev->scsi_type == scanners[i].scsi_type && + strcmp (dev->scsi_vendor, scanners[i].scsi_vendor) == 0 && + strcmp (dev->scsi_product, scanners[i].scsi_product) == 0) + { + + DBG (DBG_error, "sceptre_identify_scanner: scanner supported\n"); + + dev->scnum = i; + + return (SANE_TRUE); + } + } + + DBG (DBG_proc, "sceptre_identify_scanner: exit\n"); + + return (SANE_FALSE); +} + +/* Return the number of bytes left to read. */ +static SANE_Status +sceptre_get_status (Sceptre_Scanner * dev, size_t * data_left) +{ + size_t size; + CDB cdb; + SANE_Status status; + + DBG (DBG_proc, "sceptre_get_status: enter\n"); + + /* Get status. */ + size = 0x10; + MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size); + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, "sceptre_get_status: cannot get buffer status\n"); + *data_left = 0; + return (SANE_STATUS_IO_ERROR); + } + + if (size != 16) + { + DBG (DBG_error, + "sceptre_get_status: invalid data size returned (%ld)\n", + (long) size); + return (SANE_STATUS_IO_ERROR); + } + + hexdump (DBG_info2, "GET BUFFER STATUS result", dev->buffer, 16); + + /* Read the size left. The scanner returns the rest of the + * bytes to read, not just what's in its buffers. */ + *data_left = B32TOI (&dev->buffer[8]); + + if (dev->raster_real == 0) + { + /* First call. Set the correct parameters. */ + dev->raster_real = B16TOI (&dev->buffer[12]) * 3; + dev->params.lines = B16TOI (&dev->buffer[12]); + dev->params.pixels_per_line = B16TOI (&dev->buffer[14]); + } + + DBG (DBG_proc, "sceptre_get_status: exit, data_left=%ld\n", + (long) *data_left); + + return (SANE_STATUS_GOOD); +} + +/* + * Adjust the rasters. This function is used during a color scan, + * because the scanner does not present a format sane can interpret + * directly. + * + * The scanner sends the colors by rasters (R then G then B), whereas + * sane is waiting for a group of 3 bytes per color. To make things + * funnier, the rasters are shifted. This shift factor depends on the + * resolution used. The format of those raster is: + * R...R RG...RG RGB...RGB BG...GB B...B + * + * So this function reorders all that mess. It gets the input from + * dev->buffer and write the output in dev->image. size_in the the + * length of the valid data in dev->buffer. + */ +static void +sceptre_adjust_raster (Sceptre_Scanner * dev, size_t size_in) +{ + int nb_rasters; /* number of rasters in dev->buffer */ + + int raster; /* current raster number in buffer */ + int line; /* line number for that raster */ + int colour; /* colour for that raster */ + size_t offset; + + DBG (DBG_proc, "sceptre_adjust_raster: enter\n"); + + assert (dev->scan_mode == SCEPTRE_COLOR); + assert ((size_in % dev->params.bytes_per_line) == 0); + + if (size_in == 0) + { + return; + } + + /* + * The color coding is one line for each color (in the RGB order). + * Recombine that stuff to create a RGB value for each pixel. + */ + + nb_rasters = size_in / dev->raster_size; + + for (raster = 0; raster < nb_rasters; raster++) + { + + /* + * Find the color to which this raster belongs to. + * 0 = red + * 1 = green + * 2 = blue + * + * When blue comes, it always finishes the current line; + */ + line = 0; + if (dev->raster_num < dev->color_shift) + { + colour = 0; /* Red */ + line = dev->raster_num; + } + else if (dev->raster_num < (3 * dev->color_shift)) + { + /* even = red, odd = green */ + colour = (dev->raster_num - dev->color_shift) % 2; + if (colour) + { + /* Green */ + line = (dev->raster_num - dev->color_shift) / 2; + } + else + { + /* Red */ + line = (dev->raster_num + dev->color_shift) / 2; + } + } + else if (dev->raster_num >= dev->raster_real - dev->color_shift) + { + /* Blue */ + colour = 2; + line = dev->line; + } + else if (dev->raster_num >= dev->raster_real - 3 * dev->color_shift) + { + /* Green or Blue */ + colour = + (dev->raster_real - dev->raster_num - dev->color_shift) % 2 + 1; + if (colour == 1) + { + /* Green */ + line = dev->line + dev->color_shift; + } + else + { + /* Blue */ + line = dev->line; + } + } + else + { + colour = (dev->raster_num - 3 * dev->color_shift) % 3; + switch (colour) + { + case 0: + /* Red */ + line = (dev->raster_num + 3 * dev->color_shift) / 3; + break; + case 1: + /* Green */ + line = dev->raster_num / 3; + break; + case 2: + /* Blue */ + line = (dev->raster_num - 3 * dev->color_shift) / 3; + break; + } + } + + /* Adjust the line number relative to the image. */ + line -= dev->line; + + offset = dev->image_end + line * dev->params.bytes_per_line; + + assert (offset <= (dev->image_size - dev->raster_size)); + + /* Copy the raster to the temporary image. */ + { + int i; + unsigned char *src = dev->buffer + raster * dev->raster_size; + unsigned char *dest = dev->image + offset + colour; + + for (i = 0; i < dev->raster_size; i++) + { + *dest = *src; + src++; + dest += 3; + } + } + + if (colour == 2) + { + /* This blue raster completes a new line */ + dev->line++; + dev->image_end += dev->params.bytes_per_line; + } + + dev->raster_num++; + } + + DBG (DBG_proc, "sceptre_adjust_raster: exit\n"); +} + +/* SCSI sense handler. Callback for SANE. + * + * Since this scanner does not have REQUEST SENSE, it is always an + * error if this function is called.*/ +static SANE_Status +sceptre_sense_handler (int scsi_fd, unsigned char __sane_unused__ *result, void __sane_unused__ *arg) +{ + DBG (DBG_proc, "sceptre_sense_handler (scsi_fd = %d)\n", scsi_fd); + + return SANE_STATUS_IO_ERROR; +} + +/* Attach a scanner to this backend. */ +static SANE_Status +attach_scanner (const char *devicename, Sceptre_Scanner ** devp) +{ + Sceptre_Scanner *dev; + int sfd; + + DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename); + + if (devp) + *devp = NULL; + + /* Check if we know this device name. */ + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + if (devp) + { + *devp = dev; + } + DBG (DBG_info, "device is already known\n"); + return SANE_STATUS_GOOD; + } + } + + /* Allocate a new scanner entry. */ + dev = sceptre_init (); + if (dev == NULL) + { + DBG (DBG_error, "ERROR: not enough memory\n"); + return SANE_STATUS_NO_MEM; + } + + DBG (DBG_info, "attach_scanner: opening %s\n", devicename); + + if (sanei_scsi_open (devicename, &sfd, sceptre_sense_handler, dev) != 0) + { + DBG (DBG_error, "ERROR: attach_scanner: open failed\n"); + sceptre_free (dev); + return SANE_STATUS_INVAL; + } + + /* Fill some scanner specific values. */ + dev->devicename = strdup (devicename); + dev->sfd = sfd; + + /* Now, check that it is a scanner we support. */ + if (sceptre_identify_scanner (dev) == SANE_FALSE) + { + DBG (DBG_error, + "ERROR: attach_scanner: scanner-identification failed\n"); + sceptre_free (dev); + return SANE_STATUS_INVAL; + } + + sceptre_close (dev); + + /* Set the default options for that scanner. */ + dev->sane.name = dev->devicename; + dev->sane.vendor = scanners[dev->scnum].real_vendor; + dev->sane.model = scanners[dev->scnum].real_product; + dev->sane.type = SANE_I18N ("flatbed scanner"); + + dev->resolution_range.min = SANE_FIX (50); + dev->resolution_range.max = SANE_FIX (1200); + dev->resolution_range.quant = SANE_FIX (1); + + /* + * The S1200 has an area of 8.5 inches / 11.7 inches. (A4 like) + * That's roughly 215*297 mm + * The values are coded by + * size in inch * 600 dpi. + * The maximums are: + * X: 8.5 inches * 600 = 5100 dots + * Y: 11.7 inches * 600 = 7020 + * (although the windows driver stops at 7019) + * + * The values are stored in mm. Inches sucks anyway. + * X: 5078 dots (22 dots lost) + * Y: 7015 dots (5 dots lost) + * + * There seems to be a minimum area, but yet to be determined. + */ + dev->x_range.min = SANE_FIX (0); + dev->x_range.max = SANE_FIX (215.90); /* in mm */ + dev->x_range.quant = 0; + + dev->y_range.min = SANE_FIX (0); + dev->y_range.max = SANE_FIX (297.14); /* in mm */ + dev->y_range.quant = SANE_FIX (0); + + /* Link the scanner with the others. */ + dev->next = first_dev; + first_dev = dev; + + if (devp) + { + *devp = dev; + } + + num_devices++; + + DBG (DBG_proc, "attach_scanner: exit\n"); + + return SANE_STATUS_GOOD; +} + +static SANE_Status +attach_one (const char *dev) +{ + attach_scanner (dev, NULL); + return SANE_STATUS_GOOD; +} + +/* Reset the options for that scanner. */ +static void +sceptre_init_options (Sceptre_Scanner * dev) +{ + int i; + + DBG (DBG_proc, "sceptre_init_options: enter\n"); + + /* Pre-initialize the options. */ + memset (dev->opt, 0, sizeof (dev->opt)); + memset (dev->val, 0, sizeof (dev->val)); + + for (i = 0; i < OPT_NUM_OPTIONS; ++i) + { + dev->opt[i].size = sizeof (SANE_Word); + dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + /* Number of options. */ + dev->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS; + + /* Mode group */ + dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); + dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */ + dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_MODE_GROUP].cap = 0; + dev->opt[OPT_MODE_GROUP].size = 0; + dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* Scanner supported modes */ + dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + dev->opt[OPT_MODE].type = SANE_TYPE_STRING; + dev->opt[OPT_MODE].size = 30; /* should define yet another max_string_size() */ + dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + dev->opt[OPT_MODE].constraint.string_list = + (SANE_String_Const *) scan_mode_list; + dev->val[OPT_MODE].s = (SANE_Char *) strdup (scan_mode_list[0]); + + /* Common resolution */ + dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; + dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; + dev->opt[OPT_RESOLUTION].constraint.word_list = resolutions_list; + dev->val[OPT_RESOLUTION].w = 150; + + /* Geometry group */ + dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); + dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */ + dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; + dev->opt[OPT_GEOMETRY_GROUP].size = 0; + dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* Upper left X */ + dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED; + dev->opt[OPT_TL_X].unit = SANE_UNIT_MM; + dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_X].constraint.range = &(dev->x_range); + dev->val[OPT_TL_X].w = dev->x_range.min; + + /* Upper left Y */ + dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; + dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM; + dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_Y].constraint.range = &(dev->y_range); + dev->val[OPT_TL_Y].w = dev->y_range.min; + + /* bottom-right x */ + dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED; + dev->opt[OPT_BR_X].unit = SANE_UNIT_MM; + dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_X].constraint.range = &(dev->x_range); + dev->val[OPT_BR_X].w = dev->x_range.max; + + /* bottom-right y */ + dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; + dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM; + dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_Y].constraint.range = &(dev->y_range); + dev->val[OPT_BR_Y].w = dev->y_range.max; + + /* Enhancement group */ + dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); + dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */ + dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_ENHANCEMENT_GROUP].cap = 0; + dev->opt[OPT_ENHANCEMENT_GROUP].size = 0; + dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* custom-gamma table */ + dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + /* red gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R; + + /* green gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G; + + /* blue gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range; + dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B; + + /* Threshold */ + dev->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; + dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; + dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; + dev->opt[OPT_THRESHOLD].type = SANE_TYPE_INT; + dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; + dev->opt[OPT_THRESHOLD].size = sizeof (SANE_Int); + dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_THRESHOLD].constraint.range = &threshold_range; + dev->val[OPT_THRESHOLD].w = 128; + + /* Halftone pattern */ + dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; + dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; + dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; + dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT; + dev->opt[OPT_HALFTONE_PATTERN].size = sizeof (SANE_Int); + dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_HALFTONE_PATTERN].constraint.range = &halftone_range; + dev->val[OPT_HALFTONE_PATTERN].w = 1; + + /* preview */ + dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + dev->val[OPT_PREVIEW].w = SANE_FALSE; + + /* Lastly, set the default mode. This might change some values + * previously set here. */ + sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE, + (SANE_String *) COLOR_STR, NULL); + + DBG (DBG_proc, "sceptre_init_options: leave\n"); +} + +/* Wait until the scanner is ready. + * + * The only reason I know the scanner is not ready is because it is + * moving the CCD. + */ +static SANE_Status +sceptre_wait_scanner (Sceptre_Scanner * dev) +{ + SANE_Status status; + int timeout; + CDB cdb; + size_t size; + + DBG (DBG_proc, "sceptre_wait_scanner: enter\n"); + + MKSCSI_TEST_UNIT_READY (cdb); + cdb.data[4] = 1; /* returns one byte. Non standard SCSI. */ + + /* Set the timeout to 120 seconds. */ + timeout = 120; + + while (timeout > 0) + { + + /* test unit ready */ + size = 1; /* read one info byte */ + status = + sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status != SANE_STATUS_GOOD || size != 1) + { + DBG (DBG_error, "sceptre_wait_scanner: TUR failed\n"); + return (SANE_STATUS_IO_ERROR); + } + + /* Apparently the scanner returns only 2 values: + * 0x00 - ready + * 0xff - not ready + */ + if (dev->buffer[0] != 0x00) + { + sleep (1); /* wait 1 seconds */ + timeout--; + } + else + { + return (SANE_STATUS_GOOD); + } + }; + + DBG (DBG_proc, "sceptre_wait_scanner: scanner not ready\n"); + return (SANE_STATUS_IO_ERROR); +} + +/* Diagnostic the scanner. */ +static SANE_Status +sceptre_do_diag (Sceptre_Scanner * dev) +{ + SANE_Status status; + CDB cdb; + size_t size; + + DBG (DBG_proc, "sceptre_receive_diag enter\n"); + + /* SEND DIAGNOSTIC. */ + MKSCSI_SEND_DIAG (cdb, 0); + + /* The windows driver sets that field. This is non standard. */ + cdb.data[2] = 0x80; + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, "sceptre_do_diag: exit, status=%d\n", status); + return (status); + } + + /* RECEIVE DIAGNOSTIC */ + + /* The windows driver ask for 3 byte. This is non standard + * SCSI. The page returned should be at least 4 bytes. */ + size = 3; + MKSCSI_RECEIVE_DIAG (cdb, 0, size); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, "sceptre_do_diag: exit, status=%d\n", status); + return (status); + } + + DBG (DBG_proc, "sceptre_receive_diag exit\n"); + + return (status); +} + +/* I'm not sure if the command sent is really set mode. The SCSI + * command used is MODE SELECT, but no data is sent. Again, this is + * not standard. */ +static SANE_Status +sceptre_set_mode (Sceptre_Scanner * dev) +{ + SANE_Status status; + CDB cdb; + size_t size; + + DBG (DBG_proc, "sceptre_set_mode: enter\n"); + + size = 0x18; + MKSCSI_MODE_SELECT (cdb, 1, 0, size); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); + + DBG (DBG_proc, "sceptre_set_mode: exit, status=%d\n", status); + + return (status); +} + +/* Start a scan. */ +static SANE_Status +sceptre_scan (Sceptre_Scanner * dev) +{ + CDB cdb; + SANE_Status status; + + DBG (DBG_proc, "sceptre_scan: enter\n"); + + MKSCSI_SCAN (cdb); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); + + DBG (DBG_proc, "sceptre_scan: exit, status=%d\n", status); + + return status; +} + +/* Set a window. */ +static SANE_Status +sceptre_set_window (Sceptre_Scanner * dev) +{ + size_t size; + CDB cdb; + unsigned char window[82]; + SANE_Status status; + + DBG (DBG_proc, "sceptre_set_window: enter\n"); + + size = sizeof (window); + MKSCSI_SET_WINDOW (cdb, size); + + memset (window, 0, size); + + /* size of the parameters (74 = 0x4a bytes) */ + window[7] = sizeof (window) - 8; + + /* X and Y resolution */ + Ito16 (dev->resolution, &window[10]); + Ito16 (dev->resolution, &window[12]); + + /* Upper Left (X,Y) */ + Ito32 (dev->x_tl, &window[14]); + Ito32 (dev->y_tl, &window[18]); + + /* Width and length */ + Ito32 (dev->width, &window[22]); + Ito32 (dev->length, &window[26]); + + /* Image Composition, Halftone and Depth */ + switch (dev->scan_mode) + { + case SCEPTRE_LINEART: + window[31] = dev->val[OPT_THRESHOLD].w; + window[33] = 0; + window[34] = 1; + window[36] = 0; + break; + case SCEPTRE_HALFTONE: + window[31] = 0x80; + window[33] = 0; + window[34] = 1; + window[36] = dev->val[OPT_HALFTONE_PATTERN].w; + break; + case SCEPTRE_GRAYSCALE: + window[31] = 0x80; + window[33] = 2; + window[34] = 8; + window[36] = 0; + break; + case SCEPTRE_COLOR: + window[31] = 0x80; + window[33] = 5; + window[34] = 24; + window[36] = 0; + break; + } + + /* Unknown parameters. They look constant in the windows driver. */ + window[30] = 0x04; + window[32] = 0x04; + window[37] = 0x80; /* RIF, although it looks unused. */ + + hexdump (DBG_info2, "windows", window, sizeof (window)); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + window, sizeof (window), NULL, NULL); + + DBG (DBG_proc, "sceptre_set_window: exit, status=%d\n", status); + + return status; +} + +/* Read the image from the scanner and fill the temporary buffer with it. */ +static SANE_Status +sceptre_fill_image (Sceptre_Scanner * dev) +{ + SANE_Status status; + size_t size; + CDB cdb; + size_t data_left; + + DBG (DBG_proc, "sceptre_fill_image: enter\n"); + + assert (dev->image_begin == dev->image_end); + assert (dev->real_bytes_left > 0); + + /* Copy the complete lines, plus the imcompletes + * ones. We don't keep the real end of data used + * in image, so we copy the biggest possible. + * + * This is a no-op for non color images. + */ + memmove (dev->image, dev->image + dev->image_begin, dev->raster_ahead); + dev->image_begin = 0; + dev->image_end = 0; + + while (dev->real_bytes_left) + { + + if ((status = sceptre_get_status (dev, &data_left)) != SANE_STATUS_GOOD) + { + return (status); + } + + /* + * Try to read the maximum number of bytes. + */ + size = data_left; + if (size > dev->real_bytes_left) + { + size = dev->real_bytes_left; + } + if (size > dev->image_size - dev->raster_ahead - dev->image_end) + { + size = dev->image_size - dev->raster_ahead - dev->image_end; + } + if (size > dev->buffer_size) + { + size = dev->buffer_size; + } + + /* Round down to a multiple of line size. */ + size = size - (size % dev->params.bytes_per_line); + + if (size == 0) + { + /* Probably reached the end of the buffer. + * Check, just in case. */ + assert (dev->image_end != 0); + return (SANE_STATUS_GOOD); + } + + DBG (DBG_info, "sceptre_fill_image: to read = %ld bytes (bpl=%d)\n", + (long) size, dev->params.bytes_per_line); + + MKSCSI_READ_10 (cdb, 0, 0, size); + + hexdump (DBG_info2, "sceptre_fill_image: READ_10 CDB", cdb.data, 10); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + NULL, 0, dev->buffer, &size); + + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, + "sceptre_fill_image: cannot read from the scanner\n"); + return status; + } + + DBG (DBG_info, "sceptre_fill_image: real bytes left = %ld\n", + (long)dev->real_bytes_left); + + switch (dev->scan_mode) + { + case SCEPTRE_COLOR: + sceptre_adjust_raster (dev, size); + break; + case SCEPTRE_LINEART: + case SCEPTRE_HALFTONE: + { + /* Invert black and white. */ + unsigned char *src = dev->buffer; + unsigned char *dest = dev->image + dev->image_end; + size_t i; + for (i = 0; i < size; i++) + { + *dest = *src ^ 0xff; + dest++; + src++; + } + dev->image_end += size; + } + break; + default: + memcpy (dev->image + dev->image_end, dev->buffer, size); + dev->image_end += size; + } + + dev->real_bytes_left -= size; + } + + return (SANE_STATUS_GOOD); /* unreachable */ +} + +/* Copy from the raw buffer to the buffer given by the backend. + * + * len in input is the maximum length available in buf, and, in + * output, is the length written into buf. + */ +static void +sceptre_copy_raw_to_frontend (Sceptre_Scanner * dev, SANE_Byte * buf, + size_t * len) +{ + size_t size; + + size = dev->image_end - dev->image_begin; + if (size > *len) + { + size = *len; + } + *len = size; + + memcpy (buf, dev->image + dev->image_begin, size); + + dev->image_begin += size; +} + +/* Stop a scan. */ +static SANE_Status +do_cancel (Sceptre_Scanner * dev) +{ + DBG (DBG_sane_proc, "do_cancel enter\n"); + + if (dev->scanning == SANE_TRUE) + { + + /* Reposition the CCD. */ + dev->x_tl = 0; + dev->x_tl = 0; + dev->width = 0; + dev->length = 0; + sceptre_set_window (dev); + sceptre_scan (dev); + + sceptre_close (dev); + } + + dev->scanning = SANE_FALSE; + + DBG (DBG_sane_proc, "do_cancel exit\n"); + + return SANE_STATUS_CANCELLED; +} + +/* Start a scan. */ +static const SANE_Word gamma_init[GAMMA_LENGTH] = { + 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, + 0x21, 0x23, 0x25, 0x27, + 0x28, 0x2A, 0x2C, 0x2D, 0x2F, 0x30, 0x32, 0x33, 0x35, 0x36, 0x38, 0x39, + 0x3A, 0x3C, 0x3D, 0x3F, + 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4D, 0x4E, + 0x4F, 0x50, 0x51, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, + 0x61, 0x62, 0x63, 0x64, + 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x80, + 0x81, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, + 0x9E, 0x9F, 0x9F, 0xA0, + 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xAA, + 0xAB, 0xAC, 0xAD, 0xAD, + 0xAE, 0xAF, 0xB0, 0xB1, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xB9, 0xBA, + 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC6, + 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD2, + 0xD3, 0xD4, 0xD5, 0xD5, 0xD6, 0xD7, 0xD7, 0xD8, 0xD9, 0xDA, 0xDA, 0xDB, + 0xDC, 0xDC, 0xDD, 0xDE, + 0xDF, 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE4, 0xE4, 0xE5, 0xE6, 0xE6, + 0xE7, 0xE8, 0xE8, 0xE9, + 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, + 0xF2, 0xF3, 0xF4, 0xF4, + 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC, + 0xFD, 0xFE, 0xFE, 0xFF +}; + +static SANE_Status +sceptre_send_gamma (Sceptre_Scanner * dev) +{ + CDB cdb; + int i; + struct + { + unsigned char gamma_R[GAMMA_LENGTH]; + unsigned char gamma_G[GAMMA_LENGTH]; + unsigned char gamma_B[GAMMA_LENGTH]; + } + param; + size_t size; + SANE_Status status; + + DBG (DBG_proc, "sceptre_send_gamma: enter\n"); + + size = sizeof (param); + + assert (size == 0x300); + + MKSCSI_SEND_10 (cdb, 0x03, 0x02, size); + + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + /* Use the custom gamma. */ + for (i = 0; i < GAMMA_LENGTH; i++) + { + param.gamma_R[i] = dev->gamma_R[i]; + param.gamma_G[i] = dev->gamma_G[i]; + param.gamma_B[i] = dev->gamma_B[i]; + } + } + else + { + for (i = 0; i < GAMMA_LENGTH; i++) + { + param.gamma_R[i] = gamma_init[i]; + param.gamma_G[i] = gamma_init[i]; + param.gamma_B[i] = gamma_init[i]; + } + } + + hexdump (DBG_info2, "gamma", param.gamma_R, 3 * GAMMA_LENGTH); + + status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, + ¶m, sizeof (param), NULL, NULL); + + DBG (DBG_proc, "sceptre_send_gamma: exit, status=%d\n", status); + + return (status); +} + +/*--------------------------------------------------------------------------*/ + +/* Entry points */ + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) +{ + FILE *fp; + char dev_name[PATH_MAX]; + size_t len; + + DBG_INIT (); + + DBG (DBG_proc, "sane_init: enter\n"); + + DBG (DBG_error, "This is sane-sceptre version %d.%d-%d\n", SANE_CURRENT_MAJOR, + V_MINOR, BUILD); + DBG (DBG_error, "(C) 2002 by Frank Zago\n"); + + if (version_code) + { + *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD); + } + + fp = sanei_config_open (SCEPTRE_CONFIG_FILE); + if (!fp) + { + /* default to /dev/scanner instead of insisting on config file */ + attach_scanner ("/dev/scanner", 0); + return SANE_STATUS_GOOD; + } + + while (sanei_config_read (dev_name, sizeof (dev_name), fp)) + { + if (dev_name[0] == '#') /* ignore line comments */ + continue; + len = strlen (dev_name); + + if (!len) + continue; /* ignore empty lines */ + + sanei_config_attach_matching_devices (dev_name, attach_one); + } + + fclose (fp); + + DBG (DBG_proc, "sane_init: leave\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) +{ + Sceptre_Scanner *dev; + int i; + + DBG (DBG_proc, "sane_get_devices: enter\n"); + + if (devlist) + free (devlist); + + devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); + if (!devlist) + return SANE_STATUS_NO_MEM; + + i = 0; + for (dev = first_dev; i < num_devices; dev = dev->next) + devlist[i++] = &dev->sane; + devlist[i++] = 0; + + *device_list = devlist; + + DBG (DBG_proc, "sane_get_devices: exit\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Sceptre_Scanner *dev; + SANE_Status status; + + DBG (DBG_proc, "sane_open: enter\n"); + + /* search for devicename */ + if (devicename[0]) + { + DBG (DBG_info, "sane_open: devicename=%s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + break; + } + } + + if (!dev) + { + status = attach_scanner (devicename, &dev); + if (status != SANE_STATUS_GOOD) + { + return status; + } + } + } + else + { + DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n"); + dev = first_dev; /* empty devicename -> use first device */ + } + + if (!dev) + { + DBG (DBG_error, "No scanner found\n"); + + return SANE_STATUS_INVAL; + } + + sceptre_init_options (dev); + + /* Initialize the gamma table. */ + memcpy (dev->gamma_R, gamma_init, dev->opt[OPT_GAMMA_VECTOR_R].size); + memcpy (dev->gamma_G, gamma_init, dev->opt[OPT_GAMMA_VECTOR_G].size); + memcpy (dev->gamma_B, gamma_init, dev->opt[OPT_GAMMA_VECTOR_B].size); + + *handle = dev; + + DBG (DBG_proc, "sane_open: exit\n"); + + return SANE_STATUS_GOOD; +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Sceptre_Scanner *dev = handle; + + DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option); + + if ((unsigned) option >= OPT_NUM_OPTIONS) + { + return NULL; + } + + DBG (DBG_proc, "sane_get_option_descriptor: exit\n"); + + return dev->opt + option; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + Sceptre_Scanner *dev = handle; + SANE_Status status; + SANE_Word cap; + + DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n", + option, action); + + if (info) + { + *info = 0; + } + + if (dev->scanning) + { + return SANE_STATUS_DEVICE_BUSY; + } + + if (option < 0 || option >= OPT_NUM_OPTIONS) + { + return SANE_STATUS_INVAL; + } + + cap = dev->opt[option].cap; + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + return SANE_STATUS_INVAL; + } + + if (action == SANE_ACTION_GET_VALUE) + { + + switch (option) + { + /* word options */ + case OPT_NUM_OPTS: + case OPT_RESOLUTION: + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_THRESHOLD: + case OPT_CUSTOM_GAMMA: + case OPT_HALFTONE_PATTERN: + case OPT_PREVIEW: + + *(SANE_Word *) val = dev->val[option].w; + return SANE_STATUS_GOOD; + + /* string options */ + case OPT_MODE: + strcpy (val, dev->val[option].s); + return SANE_STATUS_GOOD; + + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + memcpy (val, dev->val[option].wa, dev->opt[option].size); + return SANE_STATUS_GOOD; + + default: + return SANE_STATUS_INVAL; + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + + if (!SANE_OPTION_IS_SETTABLE (cap)) + { + DBG (DBG_error, "could not set option, not settable\n"); + return SANE_STATUS_INVAL; + } + + status = sanei_constrain_value (dev->opt + option, val, info); + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_error, "could not set option, invalid value\n"); + return status; + } + + switch (option) + { + + /* Side-effect options */ + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_RESOLUTION: + if (info) + { + *info |= SANE_INFO_RELOAD_PARAMS; + } + dev->val[option].w = *(SANE_Word *) val; + return SANE_STATUS_GOOD; + + /* Side-effect free options */ + case OPT_THRESHOLD: + case OPT_HALFTONE_PATTERN: + case OPT_PREVIEW: + dev->val[option].w = *(SANE_Word *) val; + return SANE_STATUS_GOOD; + + case OPT_MODE: + free (dev->val[OPT_MODE].s); + dev->val[OPT_MODE].s = (SANE_Char *) strdup (val); + + /* Set default options for the scan modes. */ + dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; + + if (strcmp (dev->val[OPT_MODE].s, LINEART_STR) == 0) + { + dev->scan_mode = SCEPTRE_LINEART; + dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (dev->val[OPT_MODE].s, HALFTONE_STR) == 0) + { + dev->scan_mode = SCEPTRE_HALFTONE; + dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (dev->val[OPT_MODE].s, GRAY_STR) == 0) + { + dev->scan_mode = SCEPTRE_GRAYSCALE; + } + else if (strcmp (dev->val[OPT_MODE].s, COLOR_STR) == 0) + { + dev->scan_mode = SCEPTRE_COLOR; + dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + + if (info) + { + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + } + return SANE_STATUS_GOOD; + + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + memcpy (dev->val[option].wa, val, dev->opt[option].size); + return SANE_STATUS_GOOD; + + case OPT_CUSTOM_GAMMA: + dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val; + if (dev->val[OPT_CUSTOM_GAMMA].w) + { + /* use custom_gamma_table */ + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + else + { + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + } + if (info) + { + *info |= SANE_INFO_RELOAD_OPTIONS; + } + return SANE_STATUS_GOOD; + + default: + return SANE_STATUS_INVAL; + } + } + + DBG (DBG_proc, "sane_control_option: exit, bad\n"); + + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Sceptre_Scanner *dev = handle; + int x_dpi; /* X-Resolution */ + + DBG (DBG_proc, "sane_get_parameters: enter\n"); + + if (!(dev->scanning)) + { + /* Prepare the parameters for the caller. */ + memset (&dev->params, 0, sizeof (SANE_Parameters)); + + if (dev->val[OPT_PREVIEW].w == SANE_TRUE) + { + dev->resolution = 30; /* Windows TWAIN does 32 */ + dev->x_tl = 0; + dev->y_tl = 0; + dev->x_br = mmToIlu (SANE_UNFIX (dev->x_range.max)); + dev->y_br = mmToIlu (SANE_UNFIX (dev->y_range.max)); + } + else + { + /* Setup the parameters for the scan. These values will be re-used + * in the SET WINDOWS command. */ + dev->resolution = dev->val[OPT_RESOLUTION].w; + + dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)); + dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w)); + dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)); + dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w)); + } + + /* Check the corners are OK. */ + if (dev->x_tl > dev->x_br) + { + int s; + s = dev->x_tl; + dev->x_tl = dev->x_br; + dev->x_br = s; + } + if (dev->y_tl > dev->y_br) + { + int s; + s = dev->y_tl; + dev->y_tl = dev->y_br; + dev->y_br = s; + } + + dev->width = dev->x_br - dev->x_tl; + dev->length = dev->y_br - dev->y_tl; + + /* + * Adjust the "X Resolution". The sceptre S1200 ignores the + * Y-Resolution parameter in the windows block. X-Resolution + * is used instead. However the limits are not the same for X + * (600 dpi) and Y (1200 dpi). + */ + x_dpi = dev->resolution; + if (x_dpi > 600) + { + x_dpi = 600; + } + + /* Set depth */ + switch (dev->scan_mode) + { + case SCEPTRE_LINEART: + dev->params.format = SANE_FRAME_GRAY; + dev->depth = 1; + break; + case SCEPTRE_HALFTONE: + dev->params.format = SANE_FRAME_GRAY; + dev->depth = 1; + break; + case SCEPTRE_GRAYSCALE: + dev->params.format = SANE_FRAME_GRAY; + dev->depth = 8; + break; + case SCEPTRE_COLOR: + dev->params.format = SANE_FRAME_RGB; + dev->depth = 8; + break; + } + + /* this scanner does only one pass */ + dev->params.last_frame = SANE_TRUE; + dev->params.depth = dev->depth; + + /* Compute the number of pixels, bytes per lines and lines. */ + switch (dev->scan_mode) + { + case SCEPTRE_LINEART: + case SCEPTRE_HALFTONE: + dev->params.pixels_per_line = (dev->width * x_dpi) / 600; + dev->params.pixels_per_line &= ~0x7; /* round down to 8 */ + + dev->params.bytes_per_line = (dev->params.pixels_per_line) / 8; + + dev->params.lines = ((dev->length * dev->resolution) / 600); + if ((dev->params.lines) * 600 != (dev->length * dev->resolution)) + { + /* Round up lines to 2. */ + dev->params.lines &= ~1; + dev->params.lines += 2; + } + + break; + + case SCEPTRE_GRAYSCALE: + case SCEPTRE_COLOR: + /* pixels_per_line rounding rules: + * 2n + [0.0 .. 1.0] -> round to 2n + * 2n + ]1.0 .. 2.0] -> round to 2n + 2 + */ + dev->params.pixels_per_line = (dev->width * x_dpi) / 600; + if (dev->params.pixels_per_line & 1) + { + if ((dev->params.pixels_per_line * 600) == (dev->width * x_dpi)) + { + /* 2n */ + dev->params.pixels_per_line--; + } + else + { + /* 2n+2 */ + dev->params.pixels_per_line++; + } + } + + dev->params.bytes_per_line = dev->params.pixels_per_line; + if (dev->scan_mode == SCEPTRE_COLOR) + dev->params.bytes_per_line *= 3; + + /* lines number rounding rules: + * 2n + [0.0 .. 2.0[ -> round to 2n + * + * Note: the rounding is often incorrect at high + * resolution (ag more than 300dpi) + */ + dev->params.lines = (dev->length * dev->resolution) / 600; + dev->params.lines &= ~1; + + break; + } + + /* Find the proper color shifting parameter. */ + if (dev->scan_mode == SCEPTRE_COLOR) + { + int i = 1; + while (resolutions_list[i] != dev->resolution) + { + i++; + } + dev->color_shift = color_shift_list[i]; + } + else + { + dev->color_shift = 0; + } + + DBG (DBG_proc, "color_shift = %d\n", dev->color_shift); + + dev->bytes_left = dev->params.lines * dev->params.bytes_per_line; + } + + /* Return the current values. */ + if (params) + { + *params = (dev->params); + } + + DBG (DBG_proc, "sane_get_parameters: exit\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Sceptre_Scanner *dev = handle; + SANE_Status status; + + DBG (DBG_proc, "sane_start: enter\n"); + + if (!(dev->scanning)) + { + + sane_get_parameters (dev, NULL); + + if (dev->image) + { + free (dev->image); + } + /* Compute the length necessary in image. The first part will store + * the complete lines, and the rest is used to stored ahead + * rasters. + */ + dev->raster_ahead = + (2 * dev->color_shift + 1) * dev->params.bytes_per_line; + dev->image_size = dev->buffer_size + dev->raster_ahead; + dev->image = malloc (dev->image_size); + if (dev->image == NULL) + { + return SANE_STATUS_NO_MEM; + } + dev->image_begin = 0; + dev->image_end = 0; + + dev->raster_size = dev->params.bytes_per_line / 3; + dev->raster_num = 0; + dev->raster_real = 0; + dev->line = 0; + + /* Open again the scanner. */ + if (sanei_scsi_open + (dev->devicename, &(dev->sfd), sceptre_sense_handler, dev) != 0) + { + DBG (DBG_error, "ERROR: sane_start: open failed\n"); + return SANE_STATUS_INVAL; + } + + /* The scanner must be ready. */ + status = sceptre_wait_scanner (dev); + if (status) + { + sceptre_close (dev); + return status; + } + + status = sceptre_do_diag (dev); + if (status) + { + sceptre_close (dev); + return status; + } + + status = sceptre_set_mode (dev); + if (status) + { + sceptre_close (dev); + return status; + } + + status = sceptre_set_window (dev); + if (status) + { + sceptre_close (dev); + return status; + } + + status = sceptre_send_gamma (dev); + if (status) + { + sceptre_close (dev); + return status; + } + + status = sceptre_scan (dev); + if (status) + { + sceptre_close (dev); + return status; + } + + status = sceptre_get_status (dev, &dev->real_bytes_left); + if (status) + { + sceptre_close (dev); + return status; + } + + } + + dev->bytes_left = dev->params.bytes_per_line * dev->params.lines; + + dev->scanning = SANE_TRUE; + + DBG (DBG_proc, "sane_start: exit\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, + SANE_Int * len) +{ + SANE_Status status; + Sceptre_Scanner *dev = handle; + size_t size; + int buf_offset; /* offset into buf */ + + DBG (DBG_proc, "sane_read: enter\n"); + + *len = 0; + + if (!(dev->scanning)) + { + /* OOPS, not scanning */ + return do_cancel (dev); + } + + if (dev->bytes_left <= 0) + { + return (SANE_STATUS_EOF); + } + + buf_offset = 0; + + do + { + if (dev->image_begin == dev->image_end) + { + /* Fill image */ + status = sceptre_fill_image (dev); + if (status != SANE_STATUS_GOOD) + { + return (status); + } + } + + /* Something must have been read */ + if (dev->image_begin == dev->image_end) + { + DBG (DBG_info, "sane_read: nothing read\n"); + return SANE_STATUS_IO_ERROR; + } + + /* Copy the data to the frontend buffer. */ + size = max_len - buf_offset; + if (size > dev->bytes_left) + { + size = dev->bytes_left; + } + sceptre_copy_raw_to_frontend (dev, buf + buf_offset, &size); + + buf_offset += size; + + dev->bytes_left -= size; + *len += size; + + } + while ((buf_offset != max_len) && dev->bytes_left); + + DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long)dev->bytes_left); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) +{ + SANE_Status status; + Sceptre_Scanner *dev = handle; + + DBG (DBG_proc, "sane_set_io_mode: enter\n"); + + if (dev->scanning == SANE_FALSE) + { + return (SANE_STATUS_INVAL); + } + + if (non_blocking == SANE_FALSE) + { + status = SANE_STATUS_GOOD; + } + else + { + status = SANE_STATUS_UNSUPPORTED; + } + + DBG (DBG_proc, "sane_set_io_mode: exit\n"); + + return status; +} + +SANE_Status +sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) +{ + DBG (DBG_proc, "sane_get_select_fd: enter\n"); + + DBG (DBG_proc, "sane_get_select_fd: exit\n"); + + return SANE_STATUS_UNSUPPORTED; +} + +void +sane_cancel (SANE_Handle handle) +{ + Sceptre_Scanner *dev = handle; + + DBG (DBG_proc, "sane_cancel: enter\n"); + + do_cancel (dev); + + DBG (DBG_proc, "sane_cancel: exit\n"); +} + +void +sane_close (SANE_Handle handle) +{ + Sceptre_Scanner *dev = handle; + Sceptre_Scanner *dev_tmp; + + DBG (DBG_proc, "sane_close: enter\n"); + + do_cancel (dev); + sceptre_close (dev); + + /* Unlink dev. */ + if (first_dev == dev) + { + first_dev = dev->next; + } + else + { + dev_tmp = first_dev; + while (dev_tmp->next && dev_tmp->next != dev) + { + dev_tmp = dev_tmp->next; + } + if (dev_tmp->next != NULL) + { + dev_tmp->next = dev_tmp->next->next; + } + } + + sceptre_free (dev); + num_devices--; + + DBG (DBG_proc, "sane_close: exit\n"); +} + +void +sane_exit (void) +{ + DBG (DBG_proc, "sane_exit: enter\n"); + + while (first_dev) + { + sane_close (first_dev); + } + + if (devlist) + { + free (devlist); + devlist = NULL; + } + + DBG (DBG_proc, "sane_exit: exit\n"); +} |