summaryrefslogtreecommitdiff
path: root/backend/cardscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/cardscan.c')
-rw-r--r--backend/cardscan.c1686
1 files changed, 1686 insertions, 0 deletions
diff --git a/backend/cardscan.c b/backend/cardscan.c
new file mode 100644
index 0000000..6442458
--- /dev/null
+++ b/backend/cardscan.c
@@ -0,0 +1,1686 @@
+/* sane - Scanner Access Now Easy.
+
+ This file is part of the SANE package, and implements a SANE backend
+ for various Corex Cardscan scanners.
+
+ Copyright (C) 2007-2010 m. allan noah
+
+ 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 the Corex Cardscan 800C
+
+ 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, 2007-05-09, MAN (SANE v1.0.19)
+ - initial release
+ v1, 2008-02-14, MAN
+ - sanei_config_read has already cleaned string (#310597)
+ v2, 2010-02-10, MAN
+ - add lines_per_block config option
+ - add has_cal_buffer config option
+ - basic support for 600c
+ - clean #include lines
+
+##################################################
+ DATA FROM TRACE OF WINDOWS DRIVER:
+
+cmd packet format:
+cmdcode cmdlenlow cmdlenhigh cmdpayloadbytes
+
+resp packet format:
+respcode paperfound resplenlow resplenhigh respayloadbytes
+
+############ status read loop? ##################
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+>> 34 00 00
+<< b4 00 00 00
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+>> 34 00 00
+<< b4 00 00 00
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+
+############# scanner settings read? (0x04b8 is scan width) #############
+>> 48 00 00
+<< c8 00 0c 00 b8 04 60 00 00 80 00 00 00 58 ca 7d
+
+############## color and gray calibration data read ############
+>> 45 00 00
+<< 0x2600 bytes, bbbBBBgggGGGrrrRRRxxxXXX
+
+############ 34/b4 and 01/81 status loop til paper inserted ##############
+
+>> 35 01 00 00
+<< b5 01 01 00 00
+
+always together? {
+>> 14 05 00 80 1b 28 00 0f
+<< 94 01 05 00 80 1b 28 00 0f
+>> 22 01 00 00
+<< a2 01 01 00 00
+}
+
+>> 1a 01 00 66
+<< 9a 01 01 00 66
+
+>> 19 03 00 51 62 49
+<< 99 01 03 00 51 62 49
+
+############# heat up lamp? #################
+===========color===================
+three times {
+>> 18 07 00 00 01 60 00 61 00 07
+<< 0x40 read and 0x03 read
+the 3 byte drops from f4 f4 f4 to 17 10 08 etc.
+}
+===========gray===================
+three times {
+>> 12 06 00 00 01 60 00 61 00
+<< 0x40 read and 0x01 read
+}
+the 1 byte drops from f4 to 02
+==================================
+
+>> 35 01 00 00
+<< b5 01 01 00 00
+
+>> 13 01 00 28
+<< 93 01 01 00 28
+
+===========color===================
+three times {
+>> 18 07 00 01 10 60 00 18 05 07
+<< 0xe2c0 read
+}
+
+14/94 and 22/a2
+
+many times {
+>> 18 07 00 01 10 60 00 18 05 07
+<< 0xe2c0 read
+}
+===========gray===================
+two times {
+>> 12 06 00 01 10 60 00 18 05
+<< 0x4bc0 read
+}
+
+14/94 and 22/a2
+
+many times {
+>> 12 06 00 01 10 60 00 18 05
+<< 0x4bc0 read
+}
+==================================
+
+>> 35 01 00 ff
+<< b5 00 01 00 ff
+
+14/94 and 22/a2
+
+########### discarge capacitor? ###########
+four times {
+>> 21 02 00 0a 00
+<< a1 00 02 00 0a 00
+}
+
+>> 01 01 00 00
+<< 81 00 07 00 00 09 0c 61 c2 7a 0a
+
+>> 35 01 00 ff
+<< b5 00 01 00 ff
+
+>> 34 00 00
+<< b4 00 00 00
+#############################################
+
+ 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 "../include/sane/config.h"
+
+#include <string.h> /*memcpy...*/
+#include <ctype.h> /*isspace*/
+
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+
+#include "cardscan.h"
+
+#define DEBUG 1
+#define BUILD 2
+
+/* values for SANE_DEBUG_CARDSCAN env var:
+ - errors 5
+ - function trace 10
+ - function detail 15
+ - get/setopt cmds 20
+ - usb cmd trace 25
+ - usb cmd detail 30
+ - useless noise 35
+*/
+
+int global_has_cal_buffer = 1;
+int global_lines_per_block = 16;
+
+/* ------------------------------------------------------------------------- */
+#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
+#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+/*
+ * 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");
+
+ sanei_usb_init();
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (5, "sane_init: cardscan backend %d.%d.%d, from %s\n",
+ SANE_CURRENT_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 global 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");
+
+ global_has_cal_buffer = 1;
+ global_lines_per_block = 16;
+
+ fp = sanei_config_open (CONFIG_FILE);
+
+ if (fp) {
+
+ DBG (15, "sane_get_devices: reading config file %s\n", 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 ("usb", lp, 3) == 0) && isspace (lp[3])) {
+ DBG (15, "sane_get_devices: looking for '%s'\n", lp);
+ sanei_usb_attach_matching_devices(lp, attach_one);
+ }
+
+ else if (!strncmp(lp, "has_cal_buffer", 14) && isspace (lp[14])) {
+
+ int buf;
+ lp += 14;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if(buf){
+ global_has_cal_buffer = 1;
+ }
+ else{
+ global_has_cal_buffer = 0;
+ }
+
+ DBG (15, "sane_get_devices: setting \"has_cal_buffer\" to %d\n",
+ global_has_cal_buffer);
+ }
+
+ else if (!strncmp(lp, "lines_per_block", 15) && isspace (lp[15])) {
+
+ int buf;
+ lp += 15;
+ lp = sanei_config_skip_whitespace (lp);
+ buf = atoi (lp);
+
+ if(buf < 1 || buf > 32){
+ DBG (15,
+ "sane_get_devices: \"lines_per_block\"=%d\n out of range",
+ buf
+ );
+ continue;
+ }
+
+ DBG (15, "sane_get_devices: \"lines_per_block\" is %d\n", buf);
+ global_lines_per_block = buf;
+ }
+
+ else{
+ DBG (5, "sane_get_devices: config line \"%s\" ignored.\n", lp);
+ }
+ }
+ fclose (fp);
+ }
+
+ else {
+ DBG (5, "sane_get_devices: no config file '%s', using defaults\n",
+ CONFIG_FILE);
+
+ DBG (15, "sane_get_devices: looking for 'usb 0x08F0 0x0005'\n");
+ sanei_usb_attach_matching_devices("usb 0x08F0 0x0005", 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;
+
+ *device_list = sane_devArray;
+
+ DBG (10, "sane_get_devices: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* callback used by sane_get_devices
+ * 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, i;
+ SANE_Word vid, pid;
+
+ DBG (10, "attach_one: start '%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 scanner struct to hold it */
+ DBG (15, "attach_one: init struct\n");
+
+ if ((s = calloc (sizeof (*s), 1)) == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* copy the device name */
+ s->device_name = strdup (device_name);
+ if (!s->device_name){
+ free (s);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* connect the fd */
+ DBG (15, "attach_one: connect fd\n");
+
+ s->fd = -1;
+ ret = connect_fd(s);
+ if(ret != SANE_STATUS_GOOD){
+ free (s->device_name);
+ free (s);
+ return ret;
+ }
+
+ /* clean up the scanner struct based on model */
+ /* this is the only piece of model specific code */
+ sanei_usb_get_vendor_product(s->fd,&vid,&pid);
+
+ if(vid == 0x08f0){
+ s->vendor_name = "CardScan";
+ if(pid == 0x0005){
+ s->product_name = "800c";
+ }
+ else if(pid == 0x0002){
+ s->product_name = "600c";
+ }
+ else{
+ DBG (5, "Unknown product, using default settings\n");
+ s->product_name = "Unknown";
+ }
+ }
+ else{
+ DBG (5, "Unknown vendor/product, using default settings\n");
+ s->vendor_name = "Unknown";
+ s->product_name = "Unknown";
+ }
+
+ DBG (15, "attach_one: Found %s scanner %s at %s\n",
+ s->vendor_name, s->product_name, s->device_name);
+
+ /*copy config file settings*/
+ s->has_cal_buffer = global_has_cal_buffer;
+ s->lines_per_block = global_lines_per_block;
+ s->color_block_size = s->lines_per_block * PIXELS_PER_LINE * 3;
+ s->gray_block_size = s->lines_per_block * PIXELS_PER_LINE;
+
+ /* try to get calibration */
+ if(s->has_cal_buffer){
+ DBG (15, "attach_one: scanner calibration\n");
+
+ ret = load_calibration(s);
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: cannot calibrate, incompatible?\n");
+ free (s->device_name);
+ free (s);
+ return ret;
+ }
+ }
+ else{
+ DBG (15, "attach_one: skipping calibration\n");
+ }
+
+ /* set SANE option 'values' to good defaults */
+ DBG (15, "attach_one: init options\n");
+
+ /* go ahead and setup the first opt, because
+ * frontend may call control_option on it
+ * before calling get_option_descriptor
+ */
+ 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;
+ }
+
+ 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 (15, "attach_one: init settings\n");
+
+ /* 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;
+
+ DBG (10, "connect_fd: start\n");
+
+ if(s->fd > -1){
+ DBG (5, "connect_fd: already open\n");
+ ret = SANE_STATUS_GOOD;
+ }
+ else {
+ DBG (15, "connect_fd: opening USB device\n");
+ ret = sanei_usb_open (s->device_name, &(s->fd));
+ }
+
+ if(ret != SANE_STATUS_GOOD){
+ DBG (5, "connect_fd: could not open device: %d\n", ret);
+ }
+
+ DBG (10, "connect_fd: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+load_calibration(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ unsigned char cmd[] = {0x45, 0x00, 0x00};
+ unsigned char * buf;
+ size_t bytes = HEADER_SIZE + CAL_COLOR_SIZE*2 + CAL_GRAY_SIZE*2;
+ int j;
+
+ DBG (10, "load_calibration: start\n");
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "load_calibration: not enough mem for buffer: %ld\n",(long)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+ DBG(15, "load_calibration: got GOOD\n");
+
+ /*
+ * color cal data comes from scaner like:
+ * bbbbbbbBBBBBBBgggggggGGGGGGGrrrrrrrRRRRRRR
+ * where b=darkblue, B=lightblue, etc
+ * reorder the data into two buffers
+ * bbbbbbbgggggggrrrrrrr and BBBBBBBGGGGGGGRRRRRRR
+ */
+
+ /*dark/light blue*/
+ memcpy(s->cal_color_b, buf+HEADER_SIZE, PIXELS_PER_LINE);
+ memcpy(s->cal_color_w,
+ buf+HEADER_SIZE+PIXELS_PER_LINE, PIXELS_PER_LINE);
+
+ /*dark/light green*/
+ memcpy(s->cal_color_b+PIXELS_PER_LINE,
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*2), PIXELS_PER_LINE);
+ memcpy(s->cal_color_w+PIXELS_PER_LINE,
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*3), PIXELS_PER_LINE);
+
+ /*dark/light red*/
+ memcpy(s->cal_color_b+(PIXELS_PER_LINE*2),
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*4), PIXELS_PER_LINE);
+ memcpy(s->cal_color_w+(PIXELS_PER_LINE*2),
+ buf+HEADER_SIZE+(PIXELS_PER_LINE*5), PIXELS_PER_LINE);
+
+ /* then slide the light data down using the dark offset */
+ for(j=0;j<CAL_COLOR_SIZE;j++){
+ s->cal_color_w[j] -= s->cal_color_b[j];
+ }
+
+ /*dark/light gray*/
+ memcpy(s->cal_gray_b,
+ buf+HEADER_SIZE+(CAL_COLOR_SIZE*2), PIXELS_PER_LINE);
+ memcpy(s->cal_gray_w,
+ buf+HEADER_SIZE+(CAL_COLOR_SIZE*2)+PIXELS_PER_LINE, PIXELS_PER_LINE);
+
+ /* then slide the light data down using the dark offset */
+ for(j=0;j<CAL_GRAY_SIZE;j++){
+ s->cal_gray_w[j] -= s->cal_gray_b[j];
+ }
+
+ hexdump(35, "cal_color_b:", s->cal_color_b, CAL_COLOR_SIZE);
+ hexdump(35, "cal_color_w:", s->cal_color_w, CAL_COLOR_SIZE);
+ hexdump(35, "cal_gray_b:", s->cal_gray_b, CAL_GRAY_SIZE);
+ hexdump(35, "cal_gray_w:", s->cal_gray_w, CAL_GRAY_SIZE);
+ }
+ else {
+ DBG(5, "load_calibration: error reading data block status = %d\n", ret);
+ }
+
+ DBG (10, "load_calibration: finish\n");
+
+ return ret;
+}
+
+/*
+ * 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;
+
+ DBG (10, "sane_open: start\n");
+
+ if(name[0] == 0){
+ if(scanner_devList){
+ DBG (15, "sane_open: no device requested, using first\n");
+ s = scanner_devList;
+ }
+ else{
+ DBG (15, "sane_open: no device requested, none found\n");
+ }
+ }
+ else{
+ DBG (15, "sane_open: device %s requested, attaching\n", name);
+
+ ret = attach_one(name);
+ if(ret){
+ DBG (5, "sane_open: attach error %d\n",ret);
+ return ret;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ /* scan mode */
+ else if(option==OPT_MODE){
+ i=0;
+ s->mode_list[i++]=STRING_GRAYSCALE;
+ s->mode_list[i++]=STRING_COLOR;
+ s->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->mode_list;
+ opt->size = maxStringSize (opt->constraint.string_list);
+ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ 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_MODE:
+ if(s->mode == MODE_GRAYSCALE){
+ strcpy (val, STRING_GRAYSCALE);
+ }
+ else if(s->mode == MODE_COLOR){
+ strcpy (val, STRING_COLOR);
+ }
+ 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_MODE:
+ if (!strcmp (val, STRING_GRAYSCALE)) {
+ tmp = MODE_GRAYSCALE;
+ }
+ else{
+ tmp = MODE_COLOR;
+ }
+
+ if (tmp == s->mode)
+ return SANE_STATUS_GOOD;
+
+ s->mode = tmp;
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ 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_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ struct scanner *s = (struct scanner *) handle;
+
+ DBG (10, "sane_get_parameters: start\n");
+
+ params->pixels_per_line = PIXELS_PER_LINE;
+ params->lines = -1;
+ params->last_frame = 1;
+
+ if (s->mode == MODE_COLOR) {
+ params->format = SANE_FRAME_RGB;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line * 3;
+ }
+ else if (s->mode == MODE_GRAYSCALE) {
+ params->format = SANE_FRAME_GRAY;
+ params->depth = 8;
+ params->bytes_per_line = params->pixels_per_line;
+ }
+
+ DBG (15, "\tdepth %d\n", params->depth);
+ DBG (15, "\tlines %d\n", params->lines);
+ DBG (15, "\tpixels_per_line %d\n", params->pixels_per_line);
+ DBG (15, "\tbytes_per_line %d\n", params->bytes_per_line);
+
+ DBG (10, "sane_get_parameters: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/*
+ * Called by SANE when a page acquisition operation is to be started.
+ */
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ struct scanner *s = handle;
+ SANE_Status ret;
+
+ DBG (10, "sane_start: start\n");
+
+ /* first page of batch */
+ if(s->started){
+ DBG(5,"sane_start: previous transfer not finished?");
+ sane_cancel((SANE_Handle)s);
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* set clean defaults */
+ s->started=1;
+ s->bytes_rx=0;
+ s->bytes_tx=0;
+ s->paperless_lines=0;
+
+ /* heat up the lamp */
+ if(s->mode == MODE_COLOR){
+ ret = heat_lamp_color(s);
+ }
+ else{
+ ret = heat_lamp_gray(s);
+ }
+
+ if (ret != SANE_STATUS_GOOD) {
+ DBG (5, "sane_start: ERROR: failed to heat lamp\n");
+ sane_cancel((SANE_Handle)s);
+ return ret;
+ }
+
+ DBG (10, "sane_start: finish\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+heat_lamp_gray(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Status ret2 = SANE_STATUS_GOOD;
+ unsigned char cmd[] =
+ {0x12, 0x06, 0x00, 0x00, 0x01, 0x60, 0x00, 0x61, 0x00};
+ size_t bytes = HEADER_SIZE + 1;
+ unsigned char * buf;
+ int i;
+
+ DBG (10, "heat_lamp_gray: start\n");
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "heat_lamp_gray: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for(i=0;i<10;i++){
+
+ ret2 = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret2 != SANE_STATUS_GOOD) {
+ DBG(5, "heat_lamp_gray: %d error\n",i);
+ ret = ret2;
+ break;
+ }
+
+ if(!buf[1]){
+ DBG(5, "heat_lamp_gray: %d got no docs\n",i);
+ ret = SANE_STATUS_NO_DOCS;
+ break;
+ }
+
+ DBG(15, "heat_lamp_gray: %d got: %d %d\n",i,
+ buf[HEADER_SIZE],s->cal_gray_b[0]);
+
+ if(buf[HEADER_SIZE] < 0x20){
+ DBG(15, "heat_lamp_gray: hot\n");
+ ret = SANE_STATUS_GOOD;
+ break;
+ }
+ else{
+ DBG(15, "heat_lamp_gray: cold\n");
+ ret = SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ free(buf);
+
+ DBG (10, "heat_lamp_gray: finish %d\n",ret);
+
+ return ret;
+}
+
+static SANE_Status
+heat_lamp_color(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ SANE_Status ret2 = SANE_STATUS_GOOD;
+ unsigned char cmd[] =
+ {0x18, 0x07, 0x00, 0x00, 0x01, 0x60, 0x00, 0x61, 0x00, 0x07};
+ size_t bytes = HEADER_SIZE + 3;
+ unsigned char * buf;
+ int i;
+
+ DBG (10, "heat_lamp_color: start\n");
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "heat_lamp_color: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for(i=0;i<10;i++){
+
+ ret2 = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret2 != SANE_STATUS_GOOD) {
+ DBG(5, "heat_lamp_color: %d error\n",i);
+ ret = ret2;
+ break;
+ }
+
+ if(!buf[1]){
+ DBG(5, "heat_lamp_color: %d got no docs\n",i);
+ ret = SANE_STATUS_NO_DOCS;
+ break;
+ }
+
+ DBG(15, "heat_lamp_color: %d got: %d,%d,%d %d,%d,%d\n",i,
+ buf[HEADER_SIZE],buf[HEADER_SIZE+1],buf[HEADER_SIZE+2],
+ s->cal_color_b[0],s->cal_color_b[1],s->cal_color_b[2]);
+
+ if(buf[HEADER_SIZE] < 0x20
+ && buf[HEADER_SIZE+1] < 0x20
+ && buf[HEADER_SIZE+2] < 0x20){
+ DBG(15, "heat_lamp_color: hot\n");
+ ret = SANE_STATUS_GOOD;
+ break;
+ }
+ else{
+ DBG(15, "heat_lamp_color: cold\n");
+ ret = SANE_STATUS_DEVICE_BUSY;
+ }
+ }
+
+ free(buf);
+
+ DBG (10, "heat_lamp_color: 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=SANE_STATUS_GOOD;
+
+ DBG (10, "sane_read: start\n");
+
+ *len = 0;
+
+ /* cancelled? */
+ if(!s->started){
+ DBG (5, "sane_read: call sane_start first\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+ /* have sent all of current buffer */
+ if(s->bytes_tx == s->bytes_rx){
+
+ /* at end of data, stop */
+ if(s->paperless_lines >= MAX_PAPERLESS_LINES){
+ DBG (15, "sane_read: returning eof\n");
+ power_down(s);
+ return SANE_STATUS_EOF;
+ }
+
+ /* more to get, reset and go */
+ s->bytes_tx = 0;
+ s->bytes_rx = 0;
+
+ if(s->mode == MODE_COLOR){
+ ret = read_from_scanner_color(s);
+ }
+ else{
+ ret = read_from_scanner_gray(s);
+ }
+
+ if(ret){
+ DBG(5,"sane_read: returning %d\n",ret);
+ return ret;
+ }
+ }
+
+ /* data in current buffer, send some of it */
+ *len = s->bytes_rx - s->bytes_tx;
+ if(*len > max_len){
+ *len = max_len;
+ }
+
+ memcpy(buf,s->buffer+s->bytes_tx,*len);
+ s->bytes_tx += *len;
+
+ DBG (10, "sane_read: %d,%d,%d finish\n", *len,s->bytes_rx,s->bytes_tx);
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner_gray(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ /*cmd len-le16 move lines ??? ??? ??? ???*/
+ unsigned char cmd[] =
+ {0x12, 0x06, 0x00, 0x01, 0x01, 0x60, 0x00, 0x18, 0x05};
+ size_t bytes = HEADER_SIZE + s->gray_block_size;
+ unsigned char * buf;
+ int i,j;
+
+ DBG (10, "read_from_scanner_gray: start\n");
+
+ cmd[4] = s->lines_per_block;
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "read_from_scanner_gray: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+
+ DBG(15, "read_from_scanner_gray: got GOOD\n");
+
+ if(!buf[1]){
+ s->paperless_lines += s->lines_per_block;
+ }
+
+ s->bytes_rx = s->gray_block_size;
+
+ /*memcpy(s->buffer,buf+HEADER_SIZE,s->gray_block_size);*/
+
+ /* reorder the gray data into the struct's buffer */
+ for(i=0;i<s->gray_block_size;i+=PIXELS_PER_LINE){
+ for(j=0;j<PIXELS_PER_LINE;j++){
+
+ unsigned char byte = buf[ HEADER_SIZE + i + j ];
+ unsigned char bcal = s->cal_gray_b[j];
+ unsigned char wcal = s->cal_gray_w[j];
+
+ byte = (byte <= bcal)?0:(byte-bcal);
+ byte = (byte >= wcal)?255:(byte*255/wcal);
+ s->buffer[i+j] = byte;
+ }
+ }
+ }
+ else {
+ DBG(5, "read_from_scanner_gray: error reading status = %d\n", ret);
+ }
+
+ free(buf);
+
+ DBG (10, "read_from_scanner_gray: finish\n");
+
+ return ret;
+}
+
+static SANE_Status
+read_from_scanner_color(struct scanner *s)
+{
+ SANE_Status ret=SANE_STATUS_GOOD;
+ unsigned char cmd[] =
+ {0x18, 0x07, 0x00, 0x01, 0x01, 0x60, 0x00, 0x18, 0x05, 0x07};
+ size_t bytes = HEADER_SIZE + s->color_block_size;
+ unsigned char * buf;
+ int i,j,k;
+
+ DBG (10, "read_from_scanner_color: start\n");
+
+ cmd[4] = s->lines_per_block;
+
+ buf = malloc(bytes);
+ if(!buf){
+ DBG(5, "read_from_scanner_color: not enough mem for buffer: %lu\n",
+ (long unsigned)bytes);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if (ret == SANE_STATUS_GOOD) {
+
+ DBG(15, "read_from_scanner_color: got GOOD\n");
+
+ if(!buf[1]){
+ s->paperless_lines += s->lines_per_block;
+ }
+
+ s->bytes_rx = s->color_block_size;
+
+ /*memcpy(s->buffer,buf+HEADER_SIZE,s->color_block_size);*/
+
+ /* reorder the color data into the struct's buffer */
+ for(i=0;i<s->color_block_size;i+=PIXELS_PER_LINE*3){
+ for(j=0;j<PIXELS_PER_LINE;j++){
+ for(k=0;k<3;k++){
+
+ int offset = PIXELS_PER_LINE*(2-k) + j;
+ unsigned char byte = buf[ HEADER_SIZE + i + offset ];
+ unsigned char bcal = s->cal_color_b[offset];
+ unsigned char wcal = s->cal_color_w[offset];
+
+ byte = (byte <= bcal)?0:(byte-bcal);
+ byte = (byte >= wcal)?255:(byte*255/wcal);
+ s->buffer[i+j*3+k] = byte;
+ }
+ }
+ }
+ }
+ else {
+ DBG(5, "read_from_scanner_color: error reading status = %d\n", ret);
+ }
+
+ free(buf);
+
+ DBG (10, "read_from_scanner_color: finish\n");
+
+ return ret;
+}
+
+/*
+ * @@ Section 4 - SANE cleanup functions
+ */
+/*
+ * Cancels a scan.
+ *
+ * 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)
+{
+ struct scanner * s = (struct scanner *) handle;
+ DBG (10, "sane_cancel: start\n");
+ s->started = 0;
+ DBG (10, "sane_cancel: finish\n");
+}
+
+static SANE_Status
+power_down(struct scanner *s)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+
+ unsigned char cmd[] = {0x21, 0x02, 0x00, 0x0a, 0x00};
+ unsigned char buf[6];
+ size_t bytes = sizeof(buf);
+ int i;
+
+ DBG (10, "power_down: start\n");
+
+ for(i=0;i<5;i++){
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+
+ if(ret != SANE_STATUS_GOOD){
+ break;
+ }
+ }
+
+#if 0
+ unsigned char cmd[] = {0x35, 0x01, 0x00, 0xff};
+ unsigned char buf[5];
+ size_t bytes = sizeof(buf);
+
+ DBG (10, "power_down: start\n");
+
+ ret = do_cmd(
+ s, 0,
+ cmd, sizeof(cmd),
+ NULL, 0,
+ buf, &bytes
+ );
+#endif
+
+ DBG (10, "power_down: finish %d\n",ret);
+
+ return ret;
+}
+
+/*
+ * 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");
+
+ sane_cancel(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 usb device\n");
+ sanei_usb_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
+ */
+/*
+ * take a bunch of pointers, send commands to scanner
+ */
+static SANE_Status
+do_cmd(struct scanner *s, int shortTime,
+ unsigned char * cmdBuff, size_t cmdLen,
+ unsigned char * outBuff, size_t outLen,
+ unsigned char * inBuff, size_t * inLen
+)
+{
+ /* sanei_usb overwrites the transfer size, so make some local copies */
+ size_t loc_cmdLen = cmdLen;
+ size_t loc_outLen = outLen;
+ size_t loc_inLen = *inLen;
+
+ int cmdTime = USB_COMMAND_TIME;
+ int outTime = USB_DATA_TIME;
+ int inTime = USB_DATA_TIME;
+
+ int ret = 0;
+
+ DBG (10, "do_cmd: start\n");
+
+ if(shortTime){
+ cmdTime /= 20;
+ outTime /= 20;
+ inTime /= 20;
+ }
+
+ /* change timeout */
+ sanei_usb_set_timeout(cmdTime);
+
+ /* write the command out */
+ DBG(25, "cmd: writing %ld bytes, timeout %d\n", (long)cmdLen, cmdTime);
+ hexdump(30, "cmd: >>", cmdBuff, cmdLen);
+ ret = sanei_usb_write_bulk(s->fd, cmdBuff, &cmdLen);
+ DBG(25, "cmd: wrote %ld bytes, retVal %d\n", (long)cmdLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"cmd: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(loc_cmdLen != cmdLen){
+ DBG(5,"cmd: wrong size %ld/%ld\n", (long)loc_cmdLen, (long)cmdLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* this command has a write component, and a place to get it */
+ if(outBuff && outLen && outTime){
+
+ /* change timeout */
+ sanei_usb_set_timeout(outTime);
+
+ DBG(25, "out: writing %ld bytes, timeout %d\n", (long)outLen, outTime);
+ hexdump(30, "out: >>", outBuff, outLen);
+ ret = sanei_usb_write_bulk(s->fd, outBuff, &outLen);
+ DBG(25, "out: wrote %ld bytes, retVal %d\n", (long)outLen, ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"out: got EOF, returning IO_ERROR\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ if(ret != SANE_STATUS_GOOD){
+ DBG(5,"out: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+ if(loc_outLen != outLen){
+ DBG(5,"out: wrong size %ld/%ld\n", (long)loc_outLen, (long)outLen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ /* this command has a read component, and a place to put it */
+ if(inBuff && inLen && inTime){
+
+ memset(inBuff,0,*inLen);
+
+ /* change timeout */
+ sanei_usb_set_timeout(inTime);
+
+ DBG(25, "in: reading %ld bytes, timeout %d\n", (long)*inLen, inTime);
+ ret = sanei_usb_read_bulk(s->fd, inBuff, inLen);
+ DBG(25, "in: retVal %d\n", ret);
+
+ if(ret == SANE_STATUS_EOF){
+ DBG(5,"in: got EOF, continuing\n");
+ }
+ else if(ret != SANE_STATUS_GOOD){
+ DBG(5,"in: return error '%s'\n",sane_strstatus(ret));
+ return ret;
+ }
+
+ DBG(25, "in: read %ld bytes\n", (long)*inLen);
+ if(*inLen){
+ hexdump(30, "in: <<", inBuff, *inLen);
+ }
+
+ if(loc_inLen != *inLen){
+ ret = SANE_STATUS_EOF;
+ DBG(5,"in: short read %ld/%ld\n", (long)loc_inLen, (long)*inLen);
+ }
+ }
+
+ DBG (10, "do_cmd: finish\n");
+
+ return ret;
+}
+
+/**
+ * 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;
+}