summaryrefslogtreecommitdiff
path: root/backend/kodak.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/kodak.c')
-rw-r--r--backend/kodak.c2913
1 files changed, 2913 insertions, 0 deletions
diff --git a/backend/kodak.c b/backend/kodak.c
new file mode 100644
index 0000000..80a5700
--- /dev/null
+++ b/backend/kodak.c
@@ -0,0 +1,2913 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ --------------------------------------------------------------------------
+
+ This file implements a SANE backend for various large Kodak scanners.
+
+ The source code is divided in sections which you can easily find by
+ searching for the tag "@@".
+
+ Section 1 - Init & static stuff
+ Section 2 - sane_init, _get_devices, _open & friends
+ Section 3 - sane_*_option functions
+ Section 4 - sane_start, _get_param, _read & friends
+ Section 5 - sane_close functions
+ Section 6 - misc functions
+
+ Changes:
+ v0 through v5 2008-01-15, MAN
+ - development versions
+ v6 2009-06-22, MAN
+ - improved set_window() to build desciptor from scratch
+ - initial release
+ v7 2010-02-10, MAN
+ - add SANE_I18N to static strings
+ - don't fail if scsi buffer is too small
+
+ SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend
+ . - sane_get_devices() : query list of scanner devices
+ . - sane_open() : open a particular scanner device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get scanner fd
+ . .
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . . - sane_get_parameters() : returns estimated scan parameters
+ . . - (repeat previous 3 functions)
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times; after sane_read returns EOF,
+ . . loop may continue with sane_start which may return a 2nd page
+ . . when doing duplex scans, or load the next page from the ADF)
+ . .
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened scanner device
+ - sane_exit() : terminate use of backend
+
+*/
+
+/*
+ * @@ Section 1 - Init
+ */
+
+#include "sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_LIBC_H
+# include <libc.h> /* NeXTStep/OpenStep */
+#endif
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#include "kodak-cmd.h"
+#include "kodak.h"
+
+#define DEBUG 1
+#define BUILD 7
+
+/* values for SANE_DEBUG_KODAK env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - scsi cmd trace 25
+ - scsi cmd detail 30
+ - useless noise 35
+*/
+
+/* ------------------------------------------------------------------------- */
+#define STRING_ADFFRONT SANE_I18N("ADF Front")
+#define STRING_ADFBACK SANE_I18N("ADF Back")
+#define STRING_ADFDUPLEX SANE_I18N("ADF Duplex")
+
+#define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+/* Also set via config file. */
+static int global_buffer_size = DEFAULT_BUFFER_SIZE;
+
+/*
+ * used by attach* and sane_get_devices
+ * a ptr to a null term array of ptrs to SANE_Device structs
+ * a ptr to a single-linked list of scanner structs
+ */
+static const SANE_Device **sane_devArray = NULL;
+static struct scanner *scanner_devList = NULL;
+
+/*
+ * @@ Section 2 - SANE & scanner init code
+ */
+
+/*
+ * Called by SANE initially.
+ *
+ * From the SANE spec:
+ * This function must be called before any other SANE function can be
+ * called. The behavior of a SANE backend is undefined if this
+ * function is not called first. The version code of the backend is
+ * returned in the value pointed to by version_code. If that pointer
+ * is NULL, no version code is returned. Argument authorize is either
+ * a pointer to a function that is invoked when the backend requires
+ * authentication for a specific resource or NULL if the frontend does
+ * not support authentication.
+ */
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ authorize = authorize; /* get rid of compiler warning */
+
+ DBG_INIT ();
+ DBG (10, "sane_init: start\n");
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: kodak backend %d.%d.%d, from %s\n",
+ V_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ DBG (10, "sane_init: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE to find out about supported devices.
+ *
+ * From the SANE spec:
+ * This function can be used to query the list of devices that are
+ * available. If the function executes successfully, it stores a
+ * pointer to a NULL terminated array of pointers to SANE_Device
+ * structures in *device_list. The returned list is guaranteed to
+ * remain unchanged and valid until (a) another call to this function
+ * is performed or (b) a call to sane_exit() is performed. This
+ * function can be called repeatedly to detect when new devices become
+ * available. If argument local_only is true, only local devices are
+ * returned (devices directly attached to the machine that SANE is
+ * running on). If it is false, the device list includes all remote
+ * devices that are accessible to the SANE library.
+ *
+ * SANE does not require that this function is called before a
+ * sane_open() call is performed. A device name may be specified
+ * explicitly by a user which would make it unnecessary and
+ * undesirable to call this function first.
+ */
+/* Read the config file, find scanners with help from sanei_*
+ * store in two global lists of device structs
+ */
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ struct scanner *dev;
+ char line[PATH_MAX];
+ const char *lp;
+ FILE *fp;
+ int num_devices=0;
+ int i=0;
+
+ local_only = local_only; /* get rid of compiler warning */
+
+ DBG (10, "sane_get_devices: start\n");
+
+ /* set this to default before reading the file */
+ global_buffer_size = DEFAULT_BUFFER_SIZE;
+
+ fp = sanei_config_open (KODAK_CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n", KODAK_CONFIG_FILE);
+
+ while (sanei_config_read (line, PATH_MAX, fp)) {
+
+ lp = line;
+
+ /* ignore comments */
+ if (*lp == '#')
+ continue;
+
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ if ((strncmp ("option", lp, 6) == 0) && isspace (lp[6])) {
+
+ lp += 6;
+ lp = sanei_config_skip_whitespace (lp);
+
+ /* we allow setting buffersize too big */
+ if ((strncmp (lp, "buffer-size", 11) == 0) && isspace (lp[11])) {
+
+ int buf;
+ lp += 11;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if (buf < 4096) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" \
+ (%d) is < 4096, ignoring!\n", buf);
+ continue;
+ }
+
+ if (buf > DEFAULT_BUFFER_SIZE) {
+ DBG (5, "sane_get_devices: config option \"buffer-size\" \
+ (%d) is > %d, warning!\n", buf, DEFAULT_BUFFER_SIZE);
+ }
+
+ DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n",
+ buf);
+ global_buffer_size = buf;
+ }
+ else {
+ DBG (5, "sane_get_devices: config option \"%s\" \
+ unrecognized\n", lp);
+ }
+ }
+ else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_config_attach_matching_devices (lp, attach_one);
+ }
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" unrecognized\n",
+ lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: no config file '%s', using defaults\n",
+ KODAK_CONFIG_FILE);
+ DBG (15, "sane_get_devices: looking for 'scsi KODAK'\n");
+ sanei_config_attach_matching_devices ("scsi KODAK", attach_one);
+ }
+
+ for (dev = scanner_devList; dev; dev=dev->next) {
+ DBG (15, "sane_get_devices: found scanner %s\n",dev->device_name);
+ num_devices++;
+ }
+
+ DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
+
+ sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
+ if (!sane_devArray)
+ return SANE_STATUS_NO_MEM;
+
+ for (dev = scanner_devList; dev; dev=dev->next) {
+ sane_devArray[i++] = (SANE_Device *)&dev->sane;
+ }
+
+ sane_devArray[i] = 0;
+
+ if(device_list){
+ *device_list = sane_devArray;
+ }
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* build the scanner struct and link to global list
+ * unless struct is already loaded, then pretend
+ */
+static SANE_Status
+attach_one (const char *device_name)
+{
+ struct scanner *s;
+ int ret;
+
+ DBG (10, "attach_one: start\n");
+ DBG (15, "attach_one: looking for '%s'\n", device_name);
+
+ for (s = scanner_devList; s; s = s->next) {
+ if (strcmp (s->sane.name, device_name) == 0) {
+ DBG (10, "attach_one: already attached!\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* build a struct to hold it */
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* scsi command/data buffer */
+ s->buffer_size = global_buffer_size;
+
+ /* copy the device name */
+ s->device_name = strdup (device_name);
+ if (!s->device_name){
+ free (s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* connect the fd */
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ free (s->device_name);
+ free (s);
+ return ret;
+ }
+
+ /* Now query the device to load its vendor/model/version */
+ ret = init_inquire (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: inquiry failed\n");
+ return ret;
+ }
+
+ /* clean up the scanner struct based on model */
+ /* this is the only piece of model specific code */
+ ret = init_model (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: model failed\n");
+ return ret;
+ }
+
+ /* sets user 'values' to good defaults */
+ ret = init_user (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: user failed\n");
+ return ret;
+ }
+
+ /* sets SANE option 'values' to good defaults */
+ ret = init_options (s);
+ if (ret != SANE_STATUS_GOOD) {
+ disconnect_fd(s);
+ free (s->device_name);
+ free (s);
+ DBG (5, "attach_one: options failed\n");
+ return ret;
+ }
+
+ /* we close the connection, so that another backend can talk to scanner */
+ disconnect_fd(s);
+
+ /* load info into sane_device struct */
+ s->sane.name = s->device_name;
+ s->sane.vendor = s->vendor_name;
+ s->sane.model = s->product_name;
+ s->sane.type = "scanner";
+
+ s->next = scanner_devList;
+ scanner_devList = s;
+
+ DBG (10, "attach_one: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * connect the fd in the scanner struct
+ */
+static SANE_Status
+connect_fd (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int buffer_size = s->buffer_size;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler,
+ s, &s->buffer_size);
+ if(!ret && buffer_size != s->buffer_size){
+ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n",
+ buffer_size, s->buffer_size);
+ }
+ else{
+ DBG (15, "connect_fd: opened SCSI device\n");
+ }
+ }
+
+ DBG (10, "connect_fd: finish %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * This routine will check if a certain device is a Kodak scanner
+ * It also copies interesting data from INQUIRY into the handle structure
+ */
+static SANE_Status
+init_inquire (struct scanner *s)
+{
+ int i;
+ SANE_Status ret;
+
+ unsigned char cmd[INQUIRY_len];
+ size_t cmdLen = INQUIRY_len;
+
+ unsigned char in[I_data_len];
+ size_t inLen = I_data_len;
+
+ DBG (10, "init_inquire: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd, INQUIRY_code);
+ set_I_evpd (cmd, 0);
+ set_I_page_code (cmd, I_page_code_default);
+ set_I_data_length (cmd, inLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ in, &inLen
+ );
+
+ if (ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ if (get_I_periph_qual(in) != I_periph_qual_valid){
+ DBG (5, "The device at '%s' has invalid periph_qual.\n", s->device_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (get_I_periph_devtype(in) != I_periph_devtype_scanner){
+ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name);
+ return SANE_STATUS_INVAL;
+ }
+
+ get_I_vendor (in, s->vendor_name);
+ get_I_product (in, s->product_name);
+ get_I_version (in, s->version_name);
+ get_I_build (in, s->build_name);
+
+ s->vendor_name[8] = 0;
+ s->product_name[16] = 0;
+ s->version_name[4] = 0;
+ s->build_name[2] = 0;
+
+ /* gobble trailing spaces */
+ for (i = 7; s->vendor_name[i] == ' ' && i >= 0; i--)
+ s->vendor_name[i] = 0;
+ for (i = 15; s->product_name[i] == ' ' && i >= 0; i--)
+ s->product_name[i] = 0;
+ for (i = 3; s->version_name[i] == ' ' && i >= 0; i--)
+ s->version_name[i] = 0;
+ for (i = 2; s->build_name[i] == ' ' && i >= 0; i--)
+ s->build_name[i] = 0;
+
+ if (strcmp ("KODAK", s->vendor_name)) {
+ DBG (5, "The device at '%s' is reported to be made by '%s'\n", s->device_name, s->vendor_name);
+ DBG (5, "This backend only supports Kodak products.\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "init_inquire: Found '%s' '%s' '%s' '%s' at '%s'\n",
+ s->vendor_name, s->product_name, s->version_name, s->build_name,
+ s->device_name);
+
+ /*defined in SCSI spec*/
+ DBG (15, "standard inquiry options\n");
+
+ /*FIXME: do we need to save these?*/
+ DBG (15, " PQ: %d\n",get_I_periph_qual(in));
+ DBG (15, " PDT: %d\n",get_I_periph_devtype(in));
+
+ DBG (15, " RMB: %d\n",get_I_rmb(in));
+ DBG (15, " DTQ: %d\n",get_I_devtype_qual(in));
+
+ DBG (15, " ISO: %d\n",get_I_iso_version(in));
+ DBG (15, " ECMA: %d\n",get_I_ecma_version(in));
+ DBG (15, " ANSI: %d\n",get_I_ansi_version(in));
+
+ DBG (15, " AENC: %d\n",get_I_aenc(in));
+ DBG (15, " TrmIOP: %d\n",get_I_trmiop(in));
+ DBG (15, " RDF: %d\n",get_I_resonse_format(in));
+
+ DBG (15, " Length: %d\n",get_I_length(in));
+
+ DBG (15, " RelAdr: %d\n",get_I_reladr(in));
+ DBG (15, " WBus32: %d\n",get_I_wbus32(in));
+ DBG (15, " WBus16: %d\n",get_I_wbus16(in));
+ DBG (15, " Sync: %d\n",get_I_sync(in));
+ DBG (15, " Linked: %d\n",get_I_linked(in));
+ DBG (15, " CmdQue: %d\n",get_I_cmdque(in));
+ DBG (15, " SftRe: %d\n",get_I_sftre(in));
+
+ /*kodak specific*/
+ DBG (15, "vendor inquiry options\n");
+
+ DBG (15, " MF Disable: %d\n",get_I_mf_disable(in));
+ DBG (15, " Checkdigit: %d\n",get_I_checkdigit(in));
+ DBG (15, " Front Prism: %d\n",get_I_front_prism(in));
+ DBG (15, " Comp Gray: %d\n",get_I_compressed_gray(in));
+ DBG (15, " Front Toggle: %d\n",get_I_front_toggle(in));
+ DBG (15, " Front DP1: %d\n",get_I_front_dp1(in));
+ DBG (15, " Front Color: %d\n",get_I_front_color(in));
+ DBG (15, " Front ATP: %d\n",get_I_front_atp(in));
+
+ DBG (15, " DP1 180: %d\n",get_I_dp1_180(in));
+ DBG (15, " MF Pause: %d\n",get_I_mf_pause(in));
+ DBG (15, " Rear Prism: %d\n",get_I_rear_prism(in));
+ DBG (15, " Uncomp Gray: %d\n",get_I_uncompressed_gray(in));
+ DBG (15, " Rear Toggle: %d\n",get_I_rear_toggle(in));
+ DBG (15, " Rear DP1: %d\n",get_I_rear_dp1(in));
+ DBG (15, " Rear Color: %d\n",get_I_rear_color(in));
+ DBG (15, " Rear ATP: %d\n",get_I_rear_atp(in));
+
+ /* we actually care about these */
+ DBG (15, " Min Binary Res: %d\n",get_I_min_bin_res(in));
+ s->s_res_min[MODE_LINEART] = get_I_min_bin_res(in);
+ DBG (15, " Max Binary Res: %d\n",get_I_max_bin_res(in));
+ s->s_res_max[MODE_LINEART] = get_I_max_bin_res(in);
+ DBG (15, " Min Color Res: %d\n",get_I_min_col_res(in));
+ s->s_res_min[MODE_COLOR] = get_I_min_col_res(in);
+ DBG (15, " Max Color Res: %d\n",get_I_max_col_res(in));
+ s->s_res_max[MODE_COLOR] = get_I_max_col_res(in);
+
+ DBG (15, " Max Width: %d\n",get_I_max_image_width(in));
+ s->s_width_max = get_I_max_image_width(in);
+ DBG (15, " Max Length: %d\n",get_I_max_image_length(in));
+ s->s_length_max = get_I_max_image_length(in);
+
+ /*FIXME: do we need to save these?*/
+ DBG (15, " Finecrop: %d\n",get_I_finecrop(in));
+ DBG (15, " iThresh: %d\n",get_I_ithresh(in));
+ DBG (15, " ECD: %d\n",get_I_ecd(in));
+ DBG (15, " VBLR: %d\n",get_I_vblr(in));
+ DBG (15, " Elevator: %d\n",get_I_elevator(in));
+ DBG (15, " RelCrop: %d\n",get_I_relcrop(in));
+
+ DBG (15, " CDeskew: %d\n",get_I_cdeskew(in));
+ DBG (15, " IA: %d\n",get_I_ia(in));
+ DBG (15, " Patch: %d\n",get_I_patch(in));
+ DBG (15, " Null Mode: %d\n",get_I_nullmode(in));
+ DBG (15, " SABRE: %d\n",get_I_sabre(in));
+ DBG (15, " LDDDS: %d\n",get_I_lddds(in));
+ DBG (15, " UDDDS: %d\n",get_I_uddds(in));
+ DBG (15, " Fixed Gap: %d\n",get_I_fixedgap(in));
+
+ DBG (15, " HR Printer: %d\n",get_I_hr_printer(in));
+ DBG (15, " Elev 100/250: %d\n",get_I_elev_100_250(in));
+ DBG (15, " UDDS Individual: %d\n",get_I_udds_individual(in));
+ DBG (15, " Auto Color: %d\n",get_I_auto_color(in));
+ DBG (15, " WB: %d\n",get_I_wb(in));
+ DBG (15, " ES: %d\n",get_I_es(in));
+ DBG (15, " FC: %d\n",get_I_fc(in));
+
+ DBG (15, " Max Rate: %d\n",get_I_max_rate(in));
+ DBG (15, " Buffer Size: %d\n",get_I_buffer_size(in));
+
+ DBG (10, "init_inquire: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * get model specific info that is not in vpd, and correct
+ * errors in vpd data. struct is already initialized to 0.
+ */
+static SANE_Status
+init_model (struct scanner *s)
+{
+
+ DBG (10, "init_model: start\n");
+
+ s->s_mode[MODE_LINEART] = 1;
+ s->s_mode[MODE_HALFTONE] = 1;
+ s->s_mode[MODE_GRAYSCALE] = 1;
+ s->s_mode[MODE_COLOR] = 1;
+
+ /* scanner did not tell us these */
+ s->s_res_min[MODE_HALFTONE] = s->s_res_min[MODE_LINEART];
+ s->s_res_max[MODE_HALFTONE] = s->s_res_max[MODE_LINEART];
+
+ s->s_res_min[MODE_GRAYSCALE] = s->s_res_min[MODE_COLOR];
+ s->s_res_max[MODE_GRAYSCALE] = s->s_res_max[MODE_COLOR];
+
+ s->s_width_min = 96;
+ s->s_length_min = 96;
+
+ s->s_brightness_steps = 0;
+ s->s_contrast_steps = 255;
+ s->s_threshold_steps = 255;
+ s->s_rif = 1;
+
+ DBG (10, "init_model: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * set good default user values.
+ * struct is already initialized to 0.
+ */
+static SANE_Status
+init_user (struct scanner *s)
+{
+
+ DBG (10, "init_user: start\n");
+
+ /* source */
+ s->u_source = SOURCE_ADF_FRONT;
+
+ /* scan mode */
+ s->u_mode = MODE_LINEART;
+
+ /*res, minimum for this mode*/
+ s->u_res = s->s_res_min[s->u_mode];
+
+ /* page width US-Letter */
+ s->u_page_width = 8.5 * 1200;
+ if(s->u_page_width > s->s_width_max){
+ s->u_page_width = s->s_width_max;
+ }
+
+ /* page height US-Letter */
+ s->u_page_height = 11 * 1200;
+ if(s->u_page_height > s->s_length_max){
+ s->u_page_height = s->s_length_max;
+ }
+
+ /* bottom-right x */
+ s->u_br_x = s->u_page_width;
+
+ /* bottom-right y */
+ s->u_br_y = s->u_page_height;
+
+ DBG (10, "init_user: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This function presets the "option" array to blank
+ */
+static SANE_Status
+init_options (struct scanner *s)
+{
+ int i;
+
+ DBG (10, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ for (i = 0; i < NUM_OPTIONS; ++i) {
+ s->opt[i].name = "filler";
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_INACTIVE;
+ }
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+
+ DBG (10, "init_options: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * From the SANE spec:
+ * This function is used to establish a connection to a particular
+ * device. The name of the device to be opened is passed in argument
+ * name. If the call completes successfully, a handle for the device
+ * is returned in *h. As a special case, specifying a zero-length
+ * string as the device requests opening the first available device
+ * (if there is such a device).
+ */
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * handle)
+{
+ struct scanner *dev = NULL;
+ struct scanner *s = NULL;
+ SANE_Status ret;
+ unsigned char cmd[SEND_len];
+ size_t cmdLen = SEND_len;
+ unsigned char out[SR_len_time]; /*longest used in this function*/
+ int try=0;
+ time_t gmt_tt;
+ struct tm * gmt_tm_p;
+ struct tm * local_tm_p;
+
+ DBG (10, "sane_open: start\n");
+
+ if(scanner_devList){
+ DBG (15, "sane_open: searching currently attached scanners\n");
+ }
+ else{
+ DBG (15, "sane_open: no scanners currently attached, attaching\n");
+
+ ret = sane_get_devices(NULL,0);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+ }
+
+ if(name[0] == 0){
+ DBG (15, "sane_open: no device requested, using default\n");
+ s = scanner_devList;
+ }
+ else{
+ DBG (15, "sane_open: device %s requested\n", name);
+
+ for (dev = scanner_devList; dev; dev = dev->next) {
+ if (strcmp (dev->sane.name, name) == 0) {
+ s = dev;
+ break;
+ }
+ }
+ }
+
+ if (!s) {
+ DBG (5, "sane_open: no device found\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (15, "sane_open: device %s found\n", s->sane.name);
+
+ *handle = s;
+
+ /* connect the fd so we can talk to scanner */
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ return ret;
+ }
+
+ /*send the end batch (GX) command*/
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_end);
+ set_SR_xfer_length(cmd,SR_len_end);
+
+ /*start the following loop*/
+ ret = SANE_STATUS_DEVICE_BUSY;
+ s->rs_info = 0;
+
+ /*loop until scanner is ready*/
+ while(ret == SANE_STATUS_DEVICE_BUSY){
+ DBG (15, "sane_open: GX, try %d, sleep %lu\n", try, (unsigned long)s->rs_info);
+ try++;
+ sleep(s->rs_info);
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if(try > 5){
+ break;
+ }
+ }
+ if(ret){
+ DBG (5, "sane_open: GX error %d\n",ret);
+ return ret;
+ }
+
+ /*send the clear buffer (CB) command*/
+ DBG (15, "sane_open: CB\n");
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_clear);
+ set_SR_xfer_length(cmd,SR_len_clear);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: CB error %d\n",ret);
+ return ret;
+ }
+
+ /*send the GT command*/
+ DBG (15, "sane_open: GT\n");
+ gmt_tt = time(NULL);
+ gmt_tm_p = gmtime(&gmt_tt);
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_gmt);
+ set_SR_xfer_length(cmd,SR_len_time);
+
+ memset(out,0,SR_len_time);
+ set_SR_payload_len(out,SR_len_time);
+ set_SR_time_hour(out,gmt_tm_p->tm_hour);
+ set_SR_time_min(out,gmt_tm_p->tm_min);
+ set_SR_time_mon(out,gmt_tm_p->tm_mon);
+ set_SR_time_day(out,gmt_tm_p->tm_mday);
+ set_SR_time_year(out,gmt_tm_p->tm_year+1900);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, SR_len_time,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: GT error %d\n",ret);
+ return ret;
+ }
+
+ /*FIXME: read the LC command? */
+
+ /*send the LC command*/
+ DBG (15, "sane_open: LC\n");
+ gmt_tt = time(NULL);
+ local_tm_p = localtime(&gmt_tt);
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_clock);
+ set_SR_xfer_length(cmd,SR_len_time);
+
+ memset(out,0,SR_len_time);
+ set_SR_payload_len(out,SR_len_time);
+ set_SR_time_hour(out,local_tm_p->tm_hour);
+ set_SR_time_min(out,local_tm_p->tm_min);
+ set_SR_time_mon(out,local_tm_p->tm_mon);
+ set_SR_time_day(out,local_tm_p->tm_mday);
+ set_SR_time_year(out,local_tm_p->tm_year+1900);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ out, SR_len_time,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: LC error %d\n",ret);
+ return ret;
+ }
+
+ DBG (10, "sane_open: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * @@ Section 3 - SANE Options functions
+ */
+
+/*
+ * Returns the options we know.
+ *
+ * From the SANE spec:
+ * This function is used to access option descriptors. The function
+ * returns the option descriptor for option number n of the device
+ * represented by handle h. Option number 0 is guaranteed to be a
+ * valid option. Its value is an integer that specifies the number of
+ * options that are available for device handle h (the count includes
+ * option 0). If n is not a valid option index, the function returns
+ * NULL. The returned option descriptor is guaranteed to remain valid
+ * (and at the returned address) until the device is closed.
+ */
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ struct scanner *s = handle;
+ int i;
+ SANE_Option_Descriptor *opt = &s->opt[option];
+
+ DBG (20, "sane_get_option_descriptor: %d\n", option);
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return NULL;
+
+ /* "Mode" group -------------------------------------------------------- */
+ if(option==OPT_MODE_GROUP){
+ opt->title = "Scan Mode";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* source */
+ if(option==OPT_SOURCE){
+ i=0;
+ s->o_source_list[i++]=STRING_ADFFRONT;
+ s->o_source_list[i++]=STRING_ADFBACK;
+ s->o_source_list[i++]=STRING_ADFDUPLEX;
+ s->o_source_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_SOURCE;
+ opt->title = SANE_TITLE_SCAN_SOURCE;
+ opt->desc = SANE_DESC_SCAN_SOURCE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->o_source_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* scan mode */
+ if(option==OPT_MODE){
+ i=0;
+ if(s->s_mode[MODE_LINEART]){
+ s->o_mode_list[i++]=STRING_LINEART;
+ }
+ if(s->s_mode[MODE_HALFTONE]){
+ s->o_mode_list[i++]=STRING_HALFTONE;
+ }
+ if(s->s_mode[MODE_GRAYSCALE]){
+ s->o_mode_list[i++]=STRING_GRAYSCALE;
+ }
+ if(s->s_mode[MODE_COLOR]){
+ s->o_mode_list[i++]=STRING_COLOR;
+ }
+ s->o_mode_list[i]=NULL;
+
+ opt->name = SANE_NAME_SCAN_MODE;
+ opt->title = SANE_TITLE_SCAN_MODE;
+ opt->desc = SANE_DESC_SCAN_MODE;
+ opt->type = SANE_TYPE_STRING;
+ opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ opt->constraint.string_list = s->o_mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* resolution */
+ /* build a list of possible choices for current mode */
+ if(option==OPT_RES){
+ int reslist[]={100,150,200,240,300,400};
+ int j;
+
+ i=0;
+ for(j=0;j<6;j++){
+ if(reslist[j] >= s->s_res_min[s->u_mode]
+ && reslist[j] <= s->s_res_max[s->u_mode]){
+ s->o_res_list[s->u_mode][++i] = reslist[j];
+ }
+ }
+ s->o_res_list[s->u_mode][0] = i;
+
+ opt->name = SANE_NAME_SCAN_RESOLUTION;
+ opt->title = SANE_TITLE_SCAN_RESOLUTION;
+ opt->desc = SANE_DESC_SCAN_RESOLUTION;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_DPI;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ opt->constraint.word_list = s->o_res_list[s->u_mode];
+ }
+
+ /* "Geometry" group ---------------------------------------------------- */
+ if(option==OPT_GEOMETRY_GROUP){
+ opt->title = "Geometry";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* top-left x */
+ if(option==OPT_TL_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min);
+ s->o_tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max);
+ s->o_tl_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_X;
+ opt->title = SANE_TITLE_SCAN_TL_X;
+ opt->desc = SANE_DESC_SCAN_TL_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_tl_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* top-left y */
+ if(option==OPT_TL_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min);
+ s->o_tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max);
+ s->o_tl_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_TL_Y;
+ opt->title = SANE_TITLE_SCAN_TL_Y;
+ opt->desc = SANE_DESC_SCAN_TL_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_tl_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right x */
+ if(option==OPT_BR_X){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min);
+ s->o_br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max);
+ s->o_br_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_X;
+ opt->title = SANE_TITLE_SCAN_BR_X;
+ opt->desc = SANE_DESC_SCAN_BR_X;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_br_x_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* bottom-right y */
+ if(option==OPT_BR_Y){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min);
+ s->o_br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max);
+ s->o_br_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = SANE_NAME_SCAN_BR_Y;
+ opt->title = SANE_TITLE_SCAN_BR_Y;
+ opt->desc = SANE_DESC_SCAN_BR_Y;
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &(s->o_br_y_range);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* page width */
+ if(option==OPT_PAGE_WIDTH){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_page_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min);
+ s->o_page_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max);
+ s->o_page_x_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = "pagewidth";
+ opt->title = "ADF paper width";
+ opt->desc = "Must be set properly to align scanning window";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_page_x_range;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* page height */
+ if(option==OPT_PAGE_HEIGHT){
+ /* values stored in 1200 dpi units */
+ /* must be converted to MM for sane */
+ s->o_page_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min);
+ s->o_page_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max);
+ s->o_page_y_range.quant = MM_PER_UNIT_FIX;
+
+ opt->name = "pageheight";
+ opt->title = "ADF paper length";
+ opt->desc = "Must be set properly to eject pages";
+ opt->type = SANE_TYPE_FIXED;
+ opt->unit = SANE_UNIT_MM;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_page_y_range;
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ /* "Enhancement" group ------------------------------------------------- */
+ if(option==OPT_ENHANCEMENT_GROUP){
+ opt->title = "Enhancement";
+ opt->desc = "";
+ opt->type = SANE_TYPE_GROUP;
+ opt->constraint_type = SANE_CONSTRAINT_NONE;
+ }
+
+ /* brightness */
+ if(option==OPT_BRIGHTNESS){
+ opt->name = SANE_NAME_BRIGHTNESS;
+ opt->title = SANE_TITLE_BRIGHTNESS;
+ opt->desc = SANE_DESC_BRIGHTNESS;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_brightness_range;
+ s->o_brightness_range.quant=1;
+ s->o_brightness_range.min=-(s->s_brightness_steps/2);
+ s->o_brightness_range.max=s->s_brightness_steps/2;
+ if(opt->constraint.range->max > opt->constraint.range->min){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /* contrast */
+ if(option==OPT_CONTRAST){
+ opt->name = SANE_NAME_CONTRAST;
+ opt->title = SANE_TITLE_CONTRAST;
+ opt->desc = SANE_DESC_CONTRAST;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_contrast_range;
+ s->o_contrast_range.quant=1;
+ s->o_contrast_range.min=-(s->s_contrast_steps/2);
+ s->o_contrast_range.max=s->s_contrast_steps/2;
+ if(opt->constraint.range->max > opt->constraint.range->min){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /*threshold*/
+ if(option==OPT_THRESHOLD){
+ opt->name = SANE_NAME_THRESHOLD;
+ opt->title = SANE_TITLE_THRESHOLD;
+ opt->desc = SANE_DESC_THRESHOLD;
+ opt->type = SANE_TYPE_INT;
+ opt->unit = SANE_UNIT_NONE;
+ opt->constraint_type = SANE_CONSTRAINT_RANGE;
+ opt->constraint.range = &s->o_threshold_range;
+ s->o_threshold_range.min=0;
+ s->o_threshold_range.max=s->s_threshold_steps;
+ s->o_threshold_range.quant=1;
+ if(opt->constraint.range->max > opt->constraint.range->min){
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ else{
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+ }
+
+ /*rif*/
+ if(option==OPT_RIF){
+ opt->name = "rif";
+ opt->title = "RIF";
+ opt->desc = "Reverse image format";
+ opt->type = SANE_TYPE_BOOL;
+ opt->unit = SANE_UNIT_NONE;
+ if (s->s_rif)
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ else
+ opt->cap = SANE_CAP_INACTIVE;
+ }
+
+ return opt;
+}
+
+/**
+ * Gets or sets an option value.
+ *
+ * From the SANE spec:
+ * This function is used to set or inquire the current value of option
+ * number n of the device represented by handle h. The manner in which
+ * the option is controlled is specified by parameter action. The
+ * possible values of this parameter are described in more detail
+ * below. The value of the option is passed through argument val. It
+ * is a pointer to the memory that holds the option value. The memory
+ * area pointed to by v must be big enough to hold the entire option
+ * value (determined by member size in the corresponding option
+ * descriptor).
+ *
+ * The only exception to this rule is that when setting the value of a
+ * string option, the string pointed to by argument v may be shorter
+ * since the backend will stop reading the option value upon
+ * encountering the first NUL terminator in the string. If argument i
+ * is not NULL, the value of *i will be set to provide details on how
+ * well the request has been met.
+ */
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Int dummy = 0;
+
+ /* Make sure that all those statements involving *info cannot break (better
+ * than having to do "if (info) ..." everywhere!)
+ */
+ if (info == 0)
+ info = &dummy;
+
+ if (option >= NUM_OPTIONS) {
+ DBG (5, "sane_control_option: %d too big\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: %d inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * SANE_ACTION_GET_VALUE: We have to find out the current setting and
+ * return it in a human-readable form (often, text).
+ */
+ if (action == SANE_ACTION_GET_VALUE) {
+ SANE_Word * val_p = (SANE_Word *) val;
+
+ DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
+
+ switch (option) {
+
+ case OPT_NUM_OPTS:
+ *val_p = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if(s->u_source == SOURCE_ADF_FRONT){
+ strcpy (val, STRING_ADFFRONT);
+ }
+ else if(s->u_source == SOURCE_ADF_BACK){
+ strcpy (val, STRING_ADFBACK);
+ }
+ else if(s->u_source == SOURCE_ADF_DUPLEX){
+ strcpy (val, STRING_ADFDUPLEX);
+ }
+ else{
+ DBG(5,"missing option val for source\n");
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if(s->u_mode == MODE_LINEART){
+ strcpy (val, STRING_LINEART);
+ }
+ else if(s->u_mode == MODE_HALFTONE){
+ strcpy (val, STRING_HALFTONE);
+ }
+ else if(s->u_mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->u_mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+ *val_p = s->u_res;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_tl_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_tl_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_br_x);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_br_y);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_page_width);
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_page_height);
+ return SANE_STATUS_GOOD;
+
+ case OPT_BRIGHTNESS:
+ *val_p = s->u_brightness;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ *val_p = s->u_contrast;
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ *val_p = s->u_threshold;
+ return SANE_STATUS_GOOD;
+
+ case OPT_RIF:
+ *val_p = s->u_rif;
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE) {
+ int tmp;
+ SANE_Word val_c;
+ SANE_Status status;
+
+ DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
+
+ if ( s->started ) {
+ DBG (5, "sane_control_option: cant set, device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
+ DBG (5, "sane_control_option: not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD) {
+ DBG (5, "sane_control_option: bad value\n");
+ return status;
+ }
+
+ /* may have been changed by constrain, so dont copy until now */
+ val_c = *(SANE_Word *)val;
+
+ /*
+ * Note - for those options which can assume one of a list of
+ * valid values, we can safely assume that they will have
+ * exactly one of those values because that's what
+ * sanei_constrain_value does. Hence no "else: invalid" branches
+ * below.
+ */
+ switch (option) {
+
+ /* Mode Group */
+ case OPT_SOURCE:
+ if (!strcmp (val, STRING_ADFFRONT)) {
+ tmp = SOURCE_ADF_FRONT;
+ }
+ else if (!strcmp (val, STRING_ADFBACK)) {
+ tmp = SOURCE_ADF_BACK;
+ }
+ else{
+ tmp = SOURCE_ADF_DUPLEX;
+ }
+
+ if (s->u_source != tmp) {
+ s->u_source = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if (!strcmp (val, STRING_LINEART)) {
+ tmp = MODE_LINEART;
+ }
+ else if (!strcmp (val, STRING_HALFTONE)) {
+ tmp = MODE_HALFTONE;
+ }
+ else if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp != s->u_mode){
+ s->u_mode = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RES:
+
+ if (s->u_res != val_c) {
+ s->u_res = val_c;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ /* Geometry Group */
+ case OPT_TL_X:
+ if (s->u_tl_x != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_TL_Y:
+ if (s->u_tl_y != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_X:
+ if (s->u_br_x != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_br_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_BR_Y:
+ if (s->u_br_y != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_br_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_WIDTH:
+ if (s->u_page_width != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_page_width = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_PAGE_HEIGHT:
+ if (s->u_page_height != FIXED_MM_TO_SCANNER_UNIT(val_c)){
+ s->u_page_height = FIXED_MM_TO_SCANNER_UNIT(val_c);
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ return SANE_STATUS_GOOD;
+
+ /* Enhancement Group */
+ case OPT_BRIGHTNESS:
+ if (s->u_brightness != val_c){
+ s->u_brightness = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_CONTRAST:
+ if (s->u_contrast != val_c){
+ s->u_contrast = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_THRESHOLD:
+ if (s->u_threshold != val_c){
+ s->u_threshold = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_RIF:
+ if (s->u_rif != val_c){
+ s->u_rif = val_c;
+ }
+ return SANE_STATUS_GOOD;
+
+ } /* switch */
+ } /* else */
+
+ return SANE_STATUS_INVAL;
+}
+
+/*
+ * @@ Section 4 - SANE scanning functions
+ */
+/*
+ * Called by SANE to retrieve information about the type of data
+ * that the current scan will return.
+ *
+ * From the SANE spec:
+ * This function is used to obtain the current scan parameters. The
+ * returned parameters are guaranteed to be accurate between the time
+ * a scan has been started (sane_start() has been called) and the
+ * completion of that request. Outside of that window, the returned
+ * values are best-effort estimates of what the parameters will be
+ * when sane_start() gets invoked.
+ *
+ * Calling this function before a scan has actually started allows,
+ * for example, to get an estimate of how big the scanned image will
+ * be. The parameters passed to this function are the handle h of the
+ * device for which the parameters should be obtained and a pointer p
+ * to a parameter structure.
+ */
+/* SANE_Parameters is defined as a struct containing:
+ SANE_Frame format;
+ SANE_Bool last_frame;
+ SANE_Int lines;
+ SANE_Int depth; ( binary=1, gray=8, color=8 (!24) )
+ SANE_Int pixels_per_line;
+ SANE_Int bytes_per_line;
+*/
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ struct scanner *s = (struct scanner *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ /* started? get param data from image header */
+ if(s->started){
+ DBG (15, "sane_get_parameters: image settings:\n");
+
+ DBG (15, " tlx=%d, brx=%d, iw=%d, maxx=%d\n",
+ s->i_tlx, (s->i_tlx+s->i_width), s->i_width, s->s_width_max/1200);
+ DBG (15, " tly=%d, bry=%d, il=%d, maxy=%d\n",
+ s->i_tly, (s->i_tly+s->i_length), s->i_length, s->s_length_max/1200);
+ DBG (15, " res=%d, id=%d, bytes=%d\n",
+ s->i_dpi, s->i_id, s->i_bytes);
+
+ params->last_frame = 1;
+ params->lines = s->i_length;
+ params->pixels_per_line = s->i_width;
+
+ /* bitonal */
+ if (s->i_bpp == 1) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 1;
+ params->bytes_per_line = params->pixels_per_line / 8;
+
+#ifdef SANE_FRAME_G42D
+ /*G4 fax compression*/
+ if (s->i_compr) {
+ params->format = SANE_FRAME_G42D;
+ }
+#endif
+ }
+ /* gray */
+ else if (s->i_bpp == 8) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line;
+
+#ifdef SANE_FRAME_JPEG
+ /*jpeg compression*/
+ if (s->i_compr) {
+ params->format = SANE_FRAME_JPEG;
+ }
+#endif
+ }
+ /* color */
+ else if (s->i_bpp == 24 || s->i_bpp == 96) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line * 3;
+
+#ifdef SANE_FRAME_JPEG
+ /*jpeg compression*/
+ if (s->i_compr) {
+ params->format = SANE_FRAME_JPEG;
+ }
+#endif
+ }
+ else{
+ DBG(5,"sane_get_parameters: unsupported depth %d\n", s->i_bpp);
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ /* not started? get param data from user input */
+ else{
+
+ DBG (15, "sane_get_parameters: user settings:\n");
+
+ DBG (15, " tlx=%d, brx=%d, pw=%d, maxx=%d\n",
+ s->u_tl_x, s->u_br_x, s->u_page_width, s->s_width_max);
+ DBG (15, " tly=%d, bry=%d, ph=%d, maxy=%d\n",
+ s->u_tl_y, s->u_br_y, s->u_page_height, s->s_length_max);
+ DBG (15, " res=%d, user_x=%d, user_y=%d\n",
+ s->u_res, (s->u_res * (s->u_br_x - s->u_tl_x) / 1200),
+ (s->u_res * (s->u_br_y - s->u_tl_y) / 1200));
+
+ if (s->u_mode == MODE_COLOR) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ }
+ else if (s->u_mode == MODE_GRAYSCALE) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ }
+ else {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 1;
+ }
+
+ params->last_frame = 1;
+ params->lines = s->u_res * (s->u_br_y - s->u_tl_y) / 1200;
+ params->pixels_per_line = s->u_res * (s->u_br_x - s->u_tl_x) / 1200;
+
+ /* bytes per line differs by mode */
+ if (s->u_mode == MODE_COLOR) {
+ params->bytes_per_line = params->pixels_per_line * 3;
+ }
+ else if (s->u_mode == MODE_GRAYSCALE) {
+ params->bytes_per_line = params->pixels_per_line;
+ }
+ else {
+ params->bytes_per_line = params->pixels_per_line / 8;
+ }
+
+ }
+
+ DBG (15, "sane_get_parameters: returning:\n");
+ DBG (15, " scan_x=%d, Bpl=%d, depth=%d\n",
+ params->pixels_per_line, params->bytes_per_line, params->depth );
+
+ DBG (15, " scan_y=%d, frame=%d, last=%d\n",
+ params->lines, params->format, params->last_frame );
+
+ DBG (10, "sane_get_parameters: finish\n");
+
+ return ret;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ * commands: scanner control (lampon), send (lut), send (dither),
+ * set window, object pos, and scan
+ *
+ * this will be called before each image, including duplex backsides,
+ * and at the start of adf batch.
+ * hence, we spend alot of time playing with s->started, etc.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = handle;
+ SANE_Status ret;
+
+ DBG (10, "sane_start: start\n");
+
+ DBG (15, "started=%d, source=%d\n", s->started, s->u_source);
+
+ /* batch already running */
+ if(s->started){
+ /* not finished with current image, error */
+ if (s->bytes_tx != s->i_bytes) {
+ DBG(5,"sane_start: previous transfer not finished?");
+ return do_cancel(s);
+ }
+ }
+
+ /* first page of batch */
+ else{
+
+ unsigned char cmd[SCAN_len];
+ unsigned char pay[SR_len_startstop];
+
+ /* set window command */
+ ret = set_window(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot set window\n");
+ do_cancel(s);
+ return ret;
+ }
+
+ /* read/send JQ command */
+
+ /* read/send SC command */
+ ret = send_sc(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot send SC\n");
+ do_cancel(s);
+ return ret;
+ }
+
+ /* read/send CT command */
+
+ DBG (15, "sane_start: send SCAN\n");
+ memset(cmd, 0, SCAN_len);
+ set_SCSI_opcode(cmd, SCAN_code);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, SCAN_len,
+ NULL, 0,
+ NULL, NULL
+ );
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR sending SCAN\n");
+ do_cancel(s);
+ return ret;
+ }
+
+ /* send SS command */
+ DBG (15, "sane_start: send SS\n");
+ memset(cmd,0,SEND_len);
+ set_SCSI_opcode(cmd,SEND_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_startstop);
+ set_SR_xfer_length(cmd,SR_len_startstop);
+
+ memset(pay,0,SR_len_startstop);
+ set_SR_payload_len(pay,SR_len_startstop);
+ set_SR_startstop_cmd(pay,1);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, SEND_len,
+ pay, SR_len_startstop,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "sane_open: SS error %d\n",ret);
+ return ret;
+ }
+
+ DBG (15, "sane_start: sleeping\n");
+ sleep(2);
+
+ s->started=1;
+ }
+
+ ret = read_imageheader(s);
+ if(ret){
+ DBG (5, "sane_open: error reading imageheader %d\n",ret);
+ return ret;
+ }
+
+ /* set clean defaults */
+ s->bytes_rx = 0;
+ s->bytes_tx = 0;
+
+ /* make large buffer to hold the images */
+ DBG (15, "sane_start: setup buffer\n");
+
+ /* free current buffer if too small */
+ if (s->buffer && s->bytes_buf < s->i_bytes) {
+ DBG (15, "sane_start: free buffer.\n");
+ free(s->buffer);
+ s->buffer = NULL;
+ s->bytes_buf = 0;
+ }
+
+ /* grab new buffer if dont have one */
+ if (!s->buffer) {
+ DBG (15, "sane_start: calloc buffer.\n");
+ s->buffer = calloc (1,s->i_bytes);
+ if (!s->buffer) {
+ DBG (5, "sane_start: Error, no buffer\n");
+ do_cancel(s);
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ DBG (15, "started=%d, source=%d\n", s->started, s->u_source);
+
+ DBG (10, "sane_start: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * This routine issues a SCSI SET WINDOW command to the scanner, using the
+ * values currently in the scanner data structure.
+ * the scanner has 4 separate windows, and all must be set similarly,
+ * even if you dont intend to aquire images from all of them.
+ */
+static SANE_Status
+set_window (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[SET_WINDOW_len];
+ size_t cmdLen = SET_WINDOW_len;
+
+ /* the data phase has a header, followed by a window desc block
+ * the header specifies the number of bytes in 1 window desc block */
+ unsigned char pay[WINDOW_HEADER_len + WINDOW_DESCRIPTOR_len];
+ size_t payLen = WINDOW_HEADER_len + WINDOW_DESCRIPTOR_len;
+
+ unsigned char * desc = pay + WINDOW_HEADER_len;
+
+ int width = (s->u_br_x - s->u_tl_x) * s->u_res/1200;
+ int length = (s->u_br_y - s->u_tl_y) * s->u_res/1200;
+
+ DBG (10, "set_window: start\n");
+
+ /* binary window settings */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SET_WINDOW_code);
+ set_SW_xferlen(cmd,payLen);
+
+ memset(pay,0,payLen);
+ set_WH_desc_len(pay,WINDOW_DESCRIPTOR_len);
+
+ set_WD_wid(desc,WD_wid_front_binary);
+
+ /* common settings */
+ set_WD_Xres (desc, s->u_res);
+ set_WD_Yres (desc, s->u_res);
+
+ set_WD_ULX (desc, s->u_tl_x);
+ set_WD_ULY (desc, s->u_tl_y);
+
+ /* width % 32 == 0 && length % 1 == 0 */
+ width -= width % 32;
+ width = width*1200/s->u_res;
+
+ length = length*1200/s->u_res;
+
+ set_WD_width (desc, width);
+ set_WD_length (desc, length);
+
+ /* brightness not supported? */
+ set_WD_brightness (desc, 0);
+ set_WD_threshold (desc, s->u_threshold);
+ set_WD_contrast (desc, 0);
+ if(s->s_contrast_steps){
+ /*convert our common -127 to +127 range into HW's range
+ *FIXME: this code assumes hardware range of 1-255 */
+ set_WD_contrast (desc, s->u_contrast+128);
+ }
+
+ if(s->u_mode == MODE_HALFTONE){
+ set_WD_composition (desc, WD_compo_HALFTONE);
+ set_WD_bitsperpixel (desc, 1);
+ }
+ else{
+ set_WD_composition (desc, WD_compo_LINEART);
+ set_WD_bitsperpixel (desc, 1);
+ }
+
+ /* FIXME ht pattern */
+
+ set_WD_rif (desc, s->u_rif);
+
+ set_WD_bitorder (desc, 1);
+
+ /* compression options */
+ if(s->u_compr)
+ set_WD_compress_type (desc, WD_compr_FAXG4);
+
+ /*FIXME: noise filter */
+
+ set_WD_allow_zero(desc,1);
+ set_WD_cropping (desc, WD_crop_RELATIVE);
+
+ /*FIXME: more settings here*/
+
+ hexdump(15, "front binary window:", desc, WINDOW_DESCRIPTOR_len);
+
+ DBG (15, "set_window: set window binary back\n");
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting binary front window %d\n",ret);
+ return ret;
+ }
+
+ /*send the window for backside too*/
+ set_WD_wid(desc,WD_wid_back_binary);
+
+ DBG (15, "set_window: set window binary back\n");
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting binary back window %d\n",ret);
+ return ret;
+ }
+
+#if 0
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,GET_WINDOW_code);
+ set_GW_single(cmd,1);
+ set_GW_wid(cmd,WD_wid_front_color);
+ set_GW_xferlen(cmd,payLen);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ pay, &payLen
+ );
+ if(ret){
+ DBG (5, "set_window: error getting window %d\n",ret);
+ return ret;
+ }
+ hexdump(15,"foo",pay,payLen);
+#endif
+
+ /* color window settings */
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,SET_WINDOW_code);
+ set_SW_xferlen(cmd,payLen);
+
+ memset(pay,0,payLen);
+ set_WH_desc_len(pay,WINDOW_DESCRIPTOR_len);
+
+ set_WD_wid(desc,WD_wid_front_color);
+
+ /* common settings */
+ set_WD_Xres (desc, s->u_res);
+ set_WD_Yres (desc, s->u_res);
+
+ set_WD_ULX (desc, s->u_tl_x);
+ set_WD_ULY (desc, s->u_tl_y);
+
+ set_WD_width (desc, width);
+ set_WD_length (desc, length);
+
+ /*gray mode*/
+ if(s->u_mode == MODE_GRAYSCALE){
+ /*
+ gamma
+ width % 8 == 0 && length % 8 == 0
+ */
+ set_WD_composition (desc, WD_compo_MULTILEVEL);
+ set_WD_bitsperpixel (desc, 8);
+ }
+ /*color mode or color window in binary mode*/
+ else{
+ /*
+ width % 16 == 0 && length % 8 == 0
+ */
+ set_WD_composition (desc, WD_compo_MULTILEVEL);
+ set_WD_bitsperpixel (desc, 24);
+
+ /* compression options */
+ if(s->u_compr)
+ set_WD_compress_type (desc, WD_compr_JPEG);
+ }
+
+ set_WD_bitorder (desc, 1);
+
+ /*FIXME: noise filter */
+
+ set_WD_allow_zero(desc,1);
+ set_WD_cropping (desc, WD_crop_RELATIVE);
+
+ /*FIXME: more settings here*/
+
+ DBG (15, "set_window: set window color front\n");
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting color front window %d\n",ret);
+ return ret;
+ }
+
+ /*send the window for backside too*/
+ set_WD_wid(desc,WD_wid_back_color);
+
+ DBG (15, "set_window: set window color back\n");
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+ if(ret){
+ DBG (5, "set_window: error setting color back window %d\n",ret);
+ return ret;
+ }
+
+ DBG (10, "set_window: finish\n");
+
+ return ret;
+}
+
+/*
+ * This routine reads the SC (scanner config) data from the scanner
+ * modifies a few params based on user data, and sends it back
+ */
+static SANE_Status
+send_sc(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ size_t cmdLen = READ_len;
+ unsigned char pay[SR_len_config];
+ size_t payLen = SR_len_config;
+
+ /* send SC command */
+ DBG (10, "send_sc: start\n");
+
+ DBG (15, "send_sc: reading config\n");
+ memset(cmd,0,READ_len);
+ set_SCSI_opcode(cmd,READ_code);
+
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_config);
+ set_SR_xfer_length(cmd,SR_len_config);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ pay, &payLen
+ );
+ if(ret || !payLen){
+ DBG (5, "send_sc: error reading: %d\n",ret);
+ return ret;
+ }
+
+ memset(cmd,0,SEND_len);
+ set_SCSI_opcode(cmd,SEND_code);
+
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_config);
+ set_SR_xfer_length(cmd,payLen);
+
+ if(s->u_source == SOURCE_ADF_FRONT){
+ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){
+ set_SR_sc_io1(pay,SR_sc_io_front_color);
+ }
+ else{
+ set_SR_sc_io1(pay,SR_sc_io_front_binary);
+ }
+ set_SR_sc_io2(pay,SR_sc_io_none);
+ set_SR_sc_io3(pay,SR_sc_io_none);
+ set_SR_sc_io4(pay,SR_sc_io_none);
+ }
+ else if(s->u_source == SOURCE_ADF_BACK){
+ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){
+ set_SR_sc_io1(pay,SR_sc_io_rear_color);
+ }
+ else{
+ set_SR_sc_io1(pay,SR_sc_io_rear_binary);
+ }
+ set_SR_sc_io2(pay,SR_sc_io_none);
+ set_SR_sc_io3(pay,SR_sc_io_none);
+ set_SR_sc_io4(pay,SR_sc_io_none);
+ }
+ else{
+ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){
+ set_SR_sc_io1(pay,SR_sc_io_front_color);
+ set_SR_sc_io2(pay,SR_sc_io_rear_color);
+ }
+ else{
+ set_SR_sc_io1(pay,SR_sc_io_front_binary);
+ set_SR_sc_io2(pay,SR_sc_io_rear_binary);
+ }
+ set_SR_sc_io3(pay,SR_sc_io_none);
+ set_SR_sc_io4(pay,SR_sc_io_none);
+ }
+
+ /*FIXME: there are hundreds of other settings in this payload*/
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ pay, payLen,
+ NULL, NULL
+ );
+
+ DBG (10, "send_sc: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * This routine reads the image header from the scanner, and updates
+ * values currently in the scanner data structure.
+ */
+static SANE_Status
+read_imageheader (struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[READ_len];
+ unsigned char pay[SR_len_imageheader];
+ size_t payLen = SR_len_imageheader;
+ int pass = 0;
+
+ /* read img header */
+ DBG (10, "read_imageheader: start\n");
+
+ memset(cmd,0,READ_len);
+ set_SCSI_opcode(cmd,READ_code);
+ set_SR_datatype_code(cmd,SR_datatype_imageheader);
+ set_SR_xfer_length(cmd,SR_len_imageheader);
+
+ while (pass++ < 1000){
+
+ DBG (15, "read_imageheader: pass %d\n", pass);
+
+ payLen = SR_len_imageheader;
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, READ_len,
+ NULL, 0,
+ pay, &payLen
+ );
+
+ DBG (15, "read_imageheader: pass status %d\n", ret);
+
+ if(ret != SANE_STATUS_DEVICE_BUSY){
+ break;
+ }
+
+ usleep(50000);
+ }
+
+ if (ret == SANE_STATUS_GOOD){
+
+ DBG (15, "image header:\n");
+
+ DBG (15, " bytes: %d\n",get_SR_ih_image_length(pay));
+ s->i_bytes = get_SR_ih_image_length(pay);
+
+ DBG (15, " id: %d\n",get_SR_ih_image_id(pay));
+ s->i_id = get_SR_ih_image_id(pay);
+
+ DBG (15, " dpi: %d\n",get_SR_ih_resolution(pay));
+ s->i_dpi = get_SR_ih_resolution(pay);
+
+ DBG (15, " tlx: %d\n",get_SR_ih_ulx(pay));
+ s->i_tlx = get_SR_ih_ulx(pay);
+
+ DBG (15, " tly: %d\n",get_SR_ih_uly(pay));
+ s->i_tly = get_SR_ih_uly(pay);
+
+ DBG (15, " width: %d\n",get_SR_ih_width(pay));
+ s->i_width = get_SR_ih_width(pay);
+
+ DBG (15, " length: %d\n",get_SR_ih_length(pay));
+ s->i_length = get_SR_ih_length(pay);
+
+ DBG (15, " bpp: %d\n",get_SR_ih_bpp(pay));
+ s->i_bpp = get_SR_ih_bpp(pay);
+
+ DBG (15, " comp: %d\n",get_SR_ih_comp_type(pay));
+ s->i_compr = get_SR_ih_comp_type(pay);
+
+ /*FIXME: there are alot more of these?*/
+ }
+
+ DBG (10, "read_imageheader: finish %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Called by SANE to read data.
+ *
+ * From the SANE spec:
+ * This function is used to read image data from the device
+ * represented by handle h. Argument buf is a pointer to a memory
+ * area that is at least maxlen bytes long. The number of bytes
+ * returned is stored in *len. A backend must set this to zero when
+ * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
+ * returned).
+ *
+ * When the call succeeds, the number of bytes returned can be
+ * anywhere in the range from 0 to maxlen bytes.
+ */
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
+{
+ struct scanner *s = (struct scanner *) handle;
+ SANE_Status ret=0;
+
+ DBG (10, "sane_read: start\n");
+
+ *len=0;
+
+ /* maybe cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: not started, call sane_start\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* sane_start required between images */
+ if(s->bytes_tx == s->i_bytes){
+ DBG (15, "sane_read: returning eof\n");
+ return SANE_STATUS_EOF;
+ }
+
+ if(s->i_bytes > s->bytes_rx ){
+ ret = read_from_scanner(s);
+ if(ret){
+ DBG(5,"sane_read: returning %d\n",ret);
+ return ret;
+ }
+ }
+
+ /* copy a block from buffer to frontend */
+ ret = read_from_buffer(s,buf,max_len,len);
+
+ DBG (10, "sane_read: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int bytes = s->buffer_size;
+ int remain = s->i_bytes - s->bytes_rx;
+ unsigned char * buf;
+ size_t inLen = 0;
+
+ unsigned char cmd[READ_len];
+ int cmdLen=READ_len;
+
+ DBG (10, "read_from_scanner: start\n");
+
+ memset(cmd, 0, cmdLen);
+ set_SCSI_opcode(cmd, READ_code);
+
+ /* figure out the max amount to transfer */
+ if(bytes > remain){
+ bytes = remain;
+ }
+
+ DBG(15, "read_from_scanner: to:%d rx:%d re:%d bu:%d pa:%d\n",
+ s->i_bytes, s->bytes_rx, remain, s->buffer_size, bytes);
+
+ if(ret){
+ return ret;
+ }
+
+ inLen = bytes;
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ set_SR_datatype_code (cmd, SR_datatype_imagedata);
+ set_SR_xfer_length (cmd, bytes);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ buf, &inLen
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+ DBG(15, "read_from_scanner: got GOOD, returning GOOD\n");
+ }
+ else if (ret == SANE_STATUS_EOF) {
+ DBG(15, "read_from_scanner: got EOF, finishing\n");
+ }
+ else if (ret == SANE_STATUS_DEVICE_BUSY) {
+ DBG(5, "read_from_scanner: got BUSY, returning GOOD\n");
+ inLen = 0;
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG(5, "read_from_scanner: error reading data block status = %d\n",ret);
+ inLen = 0;
+ }
+
+ if(inLen){
+ copy_buffer (s, buf, inLen);
+ }
+
+ free(buf);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG (5, "read_from_scanner: unexpected EOF, shortening image\n");
+ s->i_bytes = s->bytes_rx;
+ ret = SANE_STATUS_GOOD;
+ }
+
+ DBG (10, "read_from_scanner: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+copy_buffer(struct scanner *s, unsigned char * buf, int len)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+
+ DBG (10, "copy_buffer: start\n");
+
+ memcpy(s->buffer+s->bytes_rx,buf,len);
+ s->bytes_rx += len;
+
+ DBG (10, "copy_buffer: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_buffer(struct scanner *s, SANE_Byte * buf,
+ SANE_Int max_len, SANE_Int * len)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ int bytes = max_len;
+ int remain = s->bytes_rx - s->bytes_tx;
+
+ DBG (10, "read_from_buffer: start\n");
+
+ /* figure out the max amount to transfer */
+ if(bytes > remain){
+ bytes = remain;
+ }
+
+ *len = bytes;
+
+ DBG(15, "read_from_buffer: to:%d tx:%d re:%d bu:%d pa:%d\n",
+ s->i_bytes, s->bytes_tx, remain, max_len, bytes);
+
+ /*FIXME this needs to timeout eventually */
+ if(!bytes){
+ DBG(5,"read_from_buffer: nothing to do\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ memcpy(buf,s->buffer+s->bytes_tx,bytes);
+
+ s->bytes_tx += *len;
+
+ DBG (10, "read_from_buffer: finish\n");
+
+ return ret;
+}
+
+
+/*
+ * @@ Section 4 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * It has been said on the mailing list that sane_cancel is a bit of a
+ * misnomer because it is routinely called to signal the end of a
+ * batch - quoting David Mosberger-Tang:
+ *
+ * > In other words, the idea is to have sane_start() be called, and
+ * > collect as many images as the frontend wants (which could in turn
+ * > consist of multiple frames each as indicated by frame-type) and
+ * > when the frontend is done, it should call sane_cancel().
+ * > Sometimes it's better to think of sane_cancel() as "sane_stop()"
+ * > but that name would have had some misleading connotations as
+ * > well, that's why we stuck with "cancel".
+ *
+ * The current consensus regarding duplex and ADF scans seems to be
+ * the following call sequence: sane_start; sane_read (repeat until
+ * EOF); sane_start; sane_read... and then call sane_cancel if the
+ * batch is at an end. I.e. do not call sane_cancel during the run but
+ * as soon as you get a SANE_STATUS_NO_DOCS.
+ *
+ * From the SANE spec:
+ * This function is used to immediately or as quickly as possible
+ * cancel the currently pending operation of the device represented by
+ * handle h. This function can be called at any time (as long as
+ * handle h is a valid handle) but usually affects long-running
+ * operations only (such as image is acquisition). It is safe to call
+ * this function asynchronously (e.g., from within a signal handler).
+ * It is important to note that completion of this operaton does not
+ * imply that the currently pending operation has been cancelled. It
+ * only guarantees that cancellation has been initiated. Cancellation
+ * completes only when the cancelled call returns (typically with a
+ * status value of SANE_STATUS_CANCELLED). Since the SANE API does
+ * not require any other operations to be re-entrant, this implies
+ * that a frontend must not call any other operation until the
+ * cancelled operation has returned.
+ */
+void
+sane_cancel (SANE_Handle handle)
+{
+ DBG (10, "sane_cancel: start\n");
+ do_cancel ((struct scanner *) handle);
+ DBG (10, "sane_cancel: finish\n");
+}
+
+/*
+ * Performs cleanup.
+ * FIXME: do better cleanup if scanning is ongoing...
+ */
+static SANE_Status
+do_cancel (struct scanner *s)
+{
+ DBG (10, "do_cancel: start\n");
+
+ s->started = 0;
+
+ DBG (10, "do_cancel: finish\n");
+
+ return SANE_STATUS_CANCELLED;
+}
+
+/*
+ * Ends use of the scanner.
+ *
+ * From the SANE spec:
+ * This function terminates the association between the device handle
+ * passed in argument h and the device it represents. If the device is
+ * presently active, a call to sane_cancel() is performed first. After
+ * this function returns, handle h must not be used anymore.
+ */
+void
+sane_close (SANE_Handle handle)
+{
+ DBG (10, "sane_close: start\n");
+
+ do_cancel((struct scanner *) handle);
+ disconnect_fd((struct scanner *) handle);
+
+ DBG (10, "sane_close: finish\n");
+}
+
+static SANE_Status
+disconnect_fd (struct scanner *s)
+{
+ DBG (10, "disconnect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (15, "disconnecting scsi device\n");
+ sanei_scsi_close (s->fd);
+ s->fd = -1;
+ }
+
+ DBG (10, "disconnect_fd: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Terminates the backend.
+ *
+ * From the SANE spec:
+ * This function must be called to terminate use of a backend. The
+ * function will first close all device handles that still might be
+ * open (it is recommended to close device handles explicitly through
+ * a call to sane_close(), but backends are required to release all
+ * resources upon a call to this function). After this function
+ * returns, no function other than sane_init() may be called
+ * (regardless of the status value returned by sane_exit(). Neglecting
+ * to call this function may result in some resources not being
+ * released properly.
+ */
+void
+sane_exit (void)
+{
+ struct scanner *dev, *next;
+
+ DBG (10, "sane_exit: start\n");
+
+ for (dev = scanner_devList; dev; dev = next) {
+ disconnect_fd(dev);
+ next = dev->next;
+ free (dev->device_name);
+ free (dev);
+ }
+
+ if (sane_devArray)
+ free (sane_devArray);
+
+ scanner_devList = NULL;
+ sane_devArray = NULL;
+
+ DBG (10, "sane_exit: finish\n");
+}
+
+
+/*
+ * @@ Section 5 - misc helper functions
+ */
+/*
+ * Called by the SANE SCSI core on device errors
+ * parses the request sense return data buffer,
+ * decides the best SANE_Status for the problem
+ * and produces debug msgs
+ */
+static SANE_Status
+sense_handler (int fd, unsigned char * sensed_data, void *arg)
+{
+ struct scanner *s = arg;
+ unsigned int ili = get_RS_ILI (sensed_data);
+ unsigned int sk = get_RS_sense_key (sensed_data);
+ unsigned int asc = get_RS_ASC (sensed_data);
+ unsigned int ascq = get_RS_ASCQ (sensed_data);
+
+ DBG (5, "sense_handler: start\n");
+
+ /* kill compiler warning */
+ fd = fd;
+
+ /* save for later */
+ s->rs_info = get_RS_information (sensed_data);
+
+ DBG (5, "SK=%#02x, ASC=%#02x, ASCQ=%#02x, ILI=%d, info=%#08lx\n",
+ sk, asc, ascq, ili, (unsigned long)s->rs_info);
+
+ switch (sk) {
+
+ /* no sense */
+ case 0x0:
+ if (0x00 != asc) {
+ DBG (5, "No sense: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x00 != ascq) {
+ DBG (5, "No sense: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (ili) {
+ DBG (5, "No sense: ILI set\n");
+ return SANE_STATUS_EOF;
+ }
+ DBG (5, "No sense: ready\n");
+ return SANE_STATUS_GOOD;
+
+ /* not ready */
+ case 0x2:
+ if (0x80 != asc) {
+ DBG (5, "Not ready: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x00 != ascq) {
+ DBG (5, "Not ready: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Not ready: end of job\n");
+ return SANE_STATUS_NO_DOCS;
+ break;
+
+ /* hardware error */
+ case 0x4:
+ if (0x3b != asc) {
+ DBG (5, "Hardware error: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x05 == ascq) {
+ DBG (5, "Hardware error: paper jam\n");
+ return SANE_STATUS_JAMMED;
+ }
+ if (0x80 == ascq) {
+ DBG (5, "Hardware error: multi-feed\n");
+ return SANE_STATUS_JAMMED;
+ }
+ DBG (5, "Hardware error: unknown ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* illegal request */
+ case 0x5:
+ if (asc != 0x20 && asc != 0x24 && asc != 0x25 && asc != 0x26
+ && asc != 0x83 && asc != 0x8f) {
+ DBG (5, "Illegal request: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x20 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid opcode\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x24 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid field in CDB\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x25 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid LUN\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x26 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: invalid field in params\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x83 == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: command failed, check log\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x83 == asc && 0x01 == ascq) {
+ DBG (5, "Illegal request: command failed, invalid state\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x83 == asc && 0x02 == ascq) {
+ DBG (5, "Illegal request: command failed, critical error\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (0x8f == asc && 0x00 == ascq) {
+ DBG (5, "Illegal request: no image\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ DBG (5, "Illegal request: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* unit attention */
+ case 0x6:
+ if (asc != 0x29 && asc != 0x80) {
+ DBG (5, "Unit attention: unknown asc\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (0x29 == asc && 0x60 == ascq) {
+ DBG (5, "Unit attention: device reset\n");
+ return SANE_STATUS_GOOD;
+ }
+ if (0x80 == asc && 0x00 == ascq) {
+ DBG (5, "Unit attention: Energy Star warm up\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x80 == asc && 0x01 == ascq) {
+ DBG (5, "Unit attention: lamp warm up for scan\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x80 == asc && 0x02 == ascq) {
+ DBG (5, "Unit attention: lamp warm up for cal\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (0x80 == asc && 0x04 == ascq) {
+ DBG (5, "Unit attention: calibration failed\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "Unit attention: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* ia overflow */
+ case 0x9:
+ if (0x80 == asc && 0x00 == ascq) {
+ DBG (5, "IA overflow: IA field overflow\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "IA overflow: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ /* volume overflow */
+ case 0xd:
+ if (0x80 == asc && 0x00 == ascq) {
+ DBG (5, "Volume overflow: Image buffer full\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (5, "Volume overflow: unknown asc/ascq\n");
+ return SANE_STATUS_IO_ERROR;
+ break;
+
+ default:
+ DBG (5, "Unknown Sense Code\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ DBG (5, "sense_handler: should never happen!\n");
+
+ return SANE_STATUS_IO_ERROR;
+}
+
+/*
+SANE_Status
+do_rs(scanner * s)
+{
+ SANE_Status ret;
+ unsigned char cmd[REQUEST_SENSE_len];
+ size_t cmdLen = REQUEST_SENSE_len;
+
+ DBG (10, "do_rs: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,REQUEST_SENSE_code);
+ set_SR_datatype_code(cmd,SR_datatype_random);
+ set_SR_datatype_qual(cmd,SR_qual_end);
+
+ ret = do_cmd (
+ s, 1, 0,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ while(ret == SANE_STATUS_DEVICE_BUSY){
+ ret = run_rs(s);
+ }
+
+ DBG (10, "do_rs: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+*/
+
+SANE_Status
+do_cmd(struct scanner *s, int runRS, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ size_t actLen = 0;
+
+ /*shut up compiler*/
+ runRS=runRS;
+ shortTime=shortTime;
+
+ DBG(10, "do_cmd: start\n");
+
+ DBG(25, "cmd: writing %d bytes\n", (int)cmdLen);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+
+ if(outBuff && outLen){
+ DBG(25, "out: writing %d bytes\n", (int)outLen);
+ hexdump(30, "out: >>", outBuff, outLen);
+ }
+ if (inBuff && inLen){
+ DBG(25, "in: reading %d bytes\n", (int)*inLen);
+ actLen = *inLen;
+ }
+
+ ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen);
+
+ if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){
+ DBG(5,"do_cmd: return '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ /* FIXME: should we look at s->rs_info here? */
+ if (inBuff && inLen){
+ hexdump(30, "in: <<", inBuff, *inLen);
+ DBG(25, "in: read %d bytes\n", (int)*inLen);
+ }
+
+ DBG(10, "do_cmd: finish\n");
+
+ return ret;
+}
+
+#if 0 /* unused */
+static SANE_Status
+wait_scanner(struct scanner *s)
+{
+ int ret;
+
+ unsigned char cmd[TEST_UNIT_READY_len];
+ size_t cmdLen = TEST_UNIT_READY_len;
+
+ DBG (10, "wait_scanner: start\n");
+
+ memset(cmd,0,cmdLen);
+ set_SCSI_opcode(cmd,TEST_UNIT_READY_code);
+
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n");
+ ret = do_cmd (
+ s, 0, 1,
+ cmd, cmdLen,
+ NULL, 0,
+ NULL, NULL
+ );
+ }
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret));
+ }
+
+ DBG (10, "wait_scanner: finish\n");
+
+ return ret;
+}
+#endif /* 0 - unused */
+
+/**
+ * Convenience method to determine longest string size in a list.
+ */
+static size_t
+maxStringSize (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+/**
+ * Prints a hex dump of the given buffer onto the debug output stream.
+ */
+static void
+hexdump (int level, char *comment, unsigned char *p, int l)
+{
+ int i;
+ char line[128];
+ char *ptr;
+
+ if(DBG_LEVEL < level)
+ return;
+
+ 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.3x:", i);
+ ptr += 4;
+ }
+ sprintf (ptr, " %2.2x", *p);
+ ptr += 3;
+ }
+ *ptr = '\0';
+ DBG (level, "%s\n", line);
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
+{
+ DBG (10, "sane_set_io_mode\n");
+ DBG (15, "%d %p\n", non_blocking, h);
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/**
+ * An advanced method we don't support but have to define.
+ */
+SANE_Status
+sane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
+{
+ DBG (10, "sane_get_select_fd\n");
+ DBG (15, "%p %d\n", h, *fdp);
+ return SANE_STATUS_UNSUPPORTED;
+}