summaryrefslogtreecommitdiff
path: root/backend/as6e.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/as6e.c')
-rw-r--r--backend/as6e.c942
1 files changed, 942 insertions, 0 deletions
diff --git a/backend/as6e.c b/backend/as6e.c
new file mode 100644
index 0000000..0fb9e31
--- /dev/null
+++ b/backend/as6e.c
@@ -0,0 +1,942 @@
+/* sane - Scanner Access Now Easy.
+ Artec AS6E backend.
+ Copyright (C) 2000 Eugene S. Weiss
+ 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 backend for the Artec AS6E by making a bridge
+ to the as6edriver program. The as6edriver program can be found at
+ http://as6edriver.sourceforge.net . */
+
+
+
+
+#include "../include/sane/config.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKENDNAME as6e
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+
+#include "as6e.h"
+
+static int num_devices;
+static AS6E_Device *first_dev;
+static AS6E_Scan *first_handle;
+static const SANE_Device **devlist = 0;
+
+static SANE_Status attach (const char *devname, AS6E_Device ** devp);
+/* static SANE_Status attach_one (const char *dev); */
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_LINEART,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ 0
+};
+
+static const SANE_Word resolution_list[] = {
+ 4, 300, 200, 100, 50
+};
+
+static const SANE_Range x_range = {
+ SANE_FIX (0),
+ SANE_FIX (215.91),
+ SANE_FIX (0)
+};
+
+static const SANE_Range y_range = {
+ SANE_FIX (0),
+ SANE_FIX (297.19),
+ SANE_FIX (0)
+};
+
+
+static const SANE_Range brightness_range = {
+ -100,
+ 100,
+ 1
+};
+
+static const SANE_Range contrast_range = {
+ -100,
+ 100,
+ 1
+};
+
+/*--------------------------------------------------------------------------*/
+static SANE_Int
+as6e_unit_convert (SANE_Fixed value)
+{
+
+ double precise;
+ SANE_Int return_value;
+
+ precise = SANE_UNFIX (value);
+ precise = (precise * 300) / MM_PER_INCH;
+ return_value = precise;
+ return return_value;
+}
+
+/*--------------------------------------------------------------------------*/
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ AS6E_Scan *s = handle;
+ SANE_Word buffer_offset = 0;
+ int written = 0, bytes_read = 0, maxbytes;
+ SANE_Word bytecounter, linebufcounter, ctlbytes;
+ SANE_Byte *linebuffer;
+
+ DBG (3, "reading %d bytes, %d bytes in carryover buffer\n", max_len,
+ s->scan_buffer_count);
+
+ if ((unsigned int) s->image_counter >= s->bytes_to_read)
+ {
+ *len = 0;
+ if (s->scanning)
+ {
+ read (s->as6e_params.ctlinpipe, &written, sizeof (written));
+ if (written != -1)
+ DBG (3, "pipe error\n");
+ DBG (3, "trying to read -1 ...written = %d\n", written);
+ }
+ s->scanning = SANE_FALSE;
+ DBG (1, "image data complete, sending EOF...\n");
+ return SANE_STATUS_EOF;
+ } /*image complete */
+
+ linebuffer = s->line_buffer;
+ if (s->scan_buffer_count > 0)
+ { /*there are leftover bytes from the last call */
+ if (s->scan_buffer_count <= max_len)
+ {
+ for (*len = 0; *len < s->scan_buffer_count; (*len)++)
+ {
+ buf[*len] = s->scan_buffer[*len];
+ buffer_offset++;
+ }
+ s->scan_buffer_count = 0;
+ if (s->scan_buffer_count == max_len)
+ {
+ s->scan_buffer_count = 0;
+ s->image_counter += max_len;
+ DBG (3, "returning %d bytes from the carryover buffer\n", *len);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ for (*len = 0; *len < max_len; (*len)++)
+ buf[*len] = s->scan_buffer[*len];
+
+ for (bytecounter = max_len;
+ bytecounter < s->scan_buffer_count; bytecounter++)
+ s->scan_buffer[bytecounter - max_len]
+ = s->scan_buffer[bytecounter];
+
+ s->scan_buffer_count -= max_len;
+ s->image_counter += max_len;
+ DBG (3, "returning %d bytes from the carryover buffer\n", *len);
+ return SANE_STATUS_GOOD;
+ }
+ }
+ else
+ {
+ *len = 0; /*no bytes in the buffer */
+ if (!s->scanning)
+ {
+ DBG (1, "scan over returning %d\n", *len);
+ if (s->scan_buffer_count)
+ return SANE_STATUS_GOOD;
+ else
+ return SANE_STATUS_EOF;
+ }
+ }
+ while (*len < max_len)
+ {
+ DBG (3, "trying to read number of bytes...\n");
+ ctlbytes = read (s->as6e_params.ctlinpipe, &written, sizeof (written));
+ DBG (3, "bytes written = %d, ctlbytes =%d\n", written, ctlbytes);
+ fflush (stdout);
+ if ((s->cancelled) && (written == 0))
+ { /*first clear -1 from pipe */
+ DBG (1, "sending SANE_STATUS_CANCELLED\n");
+ read (s->as6e_params.ctlinpipe, &written, sizeof (written));
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+ if (written == -1)
+ {
+ DBG (1, "-1READ Scanner through. returning %d bytes\n", *len);
+ s->image_counter += *len;
+ s->scanning = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+ }
+ linebufcounter = 0;
+ DBG (3,
+ "linebufctr reset, len =%d written =%d bytes_read =%d, max = %d\n",
+ *len, written, bytes_read, max_len);
+ maxbytes = written;
+ while (linebufcounter < written)
+ {
+ DBG (4, "trying to read data pipe\n");
+ bytes_read =
+ read (s->as6e_params.datapipe, linebuffer + linebufcounter,
+ maxbytes);
+ linebufcounter += bytes_read;
+ maxbytes -= bytes_read;
+ DBG (3, "bytes_read = %d linebufcounter = %d\n", bytes_read,
+ linebufcounter);
+ }
+ DBG (3, "written =%d max_len =%d len =%d\n", written, max_len, *len);
+ if (written <= (max_len - *len))
+ {
+ for (bytecounter = 0; bytecounter < written; bytecounter++)
+ {
+ buf[bytecounter + buffer_offset] = linebuffer[bytecounter];
+ (*len)++;
+ }
+ buffer_offset += written;
+ DBG (3, "buffer offset = %d\n", buffer_offset);
+ }
+ else if (max_len > *len)
+ { /*there's still room to send data */
+ for (bytecounter = 0; bytecounter < (max_len - *len); bytecounter++)
+ buf[bytecounter + buffer_offset] = linebuffer[bytecounter];
+ DBG (3, "topping off buffer\n");
+ for (bytecounter = (max_len - *len); bytecounter < written;
+ bytecounter++)
+ {
+
+ s->scan_buffer[s->scan_buffer_count + bytecounter -
+ (max_len - *len)] = linebuffer[bytecounter];
+ }
+ s->scan_buffer_count += (written - (max_len - *len));
+ *len = max_len;
+ }
+ else
+ { /*everything goes into the carryover buffer */
+ for (bytecounter = 0; bytecounter < written; bytecounter++)
+ s->scan_buffer[s->scan_buffer_count + bytecounter]
+ = linebuffer[bytecounter];
+ s->scan_buffer_count += written;
+ }
+ } /*while there's space in the buffer */
+ s->image_counter += *len;
+ DBG (3, "image ctr = %d bytes_to_read = %lu returning %d\n",
+ s->image_counter, (u_long) s->bytes_to_read, *len);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*--------------------------------------------------------------------------*/
+void
+sane_cancel (SANE_Handle h)
+{
+ AS6E_Scan *s = h;
+ SANE_Word test;
+ DBG (2, "trying to cancel...\n");
+ if (s->scanning)
+ {
+ test = kill (s->child_pid, SIGUSR1);
+ if (test == 0)
+ s->cancelled = SANE_TRUE;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ AS6E_Scan *s = handle;
+ SANE_Status status;
+ int repeat = 1;
+ SANE_Word numbytes;
+ int scan_params[8];
+ /* First make sure we have a current parameter set. Some of the
+ * parameters will be overwritten below, but that's OK. */
+ DBG (2, "sane_start\n");
+ status = sane_get_parameters (s, 0);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ DBG (1, "Got params again...\n");
+ numbytes = write (s->as6e_params.ctloutpipe, &repeat, sizeof (repeat));
+ if (numbytes != sizeof (repeat))
+ return (SANE_STATUS_IO_ERROR);
+ DBG (1, "sending start_scan signal\n");
+ scan_params[0] = s->as6e_params.resolution;
+ if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ scan_params[1] = 0;
+ else if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ scan_params[1] = 1;
+ else if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ scan_params[1] = 2;
+ else
+ return (SANE_STATUS_JAMMED); /*this should never happen */
+ scan_params[2] = s->as6e_params.startpos;
+ scan_params[3] = s->as6e_params.stoppos;
+ scan_params[4] = s->as6e_params.startline;
+ scan_params[5] = s->as6e_params.stopline;
+ scan_params[6] = s->value[OPT_BRIGHTNESS].w;
+ scan_params[7] = s->value[OPT_CONTRAST].w;
+ DBG (1, "scan params = %d %d %d %d %d %d %d %d\n", scan_params[0],
+ scan_params[1], scan_params[2], scan_params[3],
+ scan_params[4], scan_params[5], scan_params[6], scan_params[7]);
+ numbytes =
+ write (s->as6e_params.ctloutpipe, scan_params, sizeof (scan_params));
+ if (numbytes != sizeof (scan_params))
+ return (SANE_STATUS_IO_ERROR);
+ s->scanning = SANE_TRUE;
+ s->scan_buffer_count = 0;
+ s->image_counter = 0;
+ s->cancelled = 0;
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ AS6E_Scan *s = handle;
+ SANE_String mode;
+ SANE_Word divisor = 1;
+ DBG (2, "sane_get_parameters\n");
+ if (!s->scanning)
+ {
+ memset (&s->sane_params, 0, sizeof (s->sane_params));
+ s->as6e_params.resolution = s->value[OPT_RESOLUTION].w;
+ s->as6e_params.startpos = as6e_unit_convert (s->value[OPT_TL_X].w);
+ s->as6e_params.stoppos = as6e_unit_convert (s->value[OPT_BR_X].w);
+ s->as6e_params.startline = as6e_unit_convert (s->value[OPT_TL_Y].w);
+ s->as6e_params.stopline = as6e_unit_convert (s->value[OPT_BR_Y].w);
+ if ((s->as6e_params.resolution == 200)
+ || (s->as6e_params.resolution == 100))
+ divisor = 3;
+ else if (s->as6e_params.resolution == 50)
+ divisor = 6; /*get legal values for 200 dpi */
+ s->as6e_params.startpos = (s->as6e_params.startpos / divisor) * divisor;
+ s->as6e_params.stoppos = (s->as6e_params.stoppos / divisor) * divisor;
+ s->as6e_params.startline =
+ (s->as6e_params.startline / divisor) * divisor;
+ s->as6e_params.stopline = (s->as6e_params.stopline / divisor) * divisor;
+ s->sane_params.pixels_per_line =
+ (s->as6e_params.stoppos -
+ s->as6e_params.startpos) * s->as6e_params.resolution / 300;
+ s->sane_params.lines =
+ (s->as6e_params.stopline -
+ s->as6e_params.startline) * s->as6e_params.resolution / 300;
+ mode = s->value[OPT_MODE].s;
+/* if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) ||
+ (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0))
+ {
+ s->sane_params.format = SANE_FRAME_GRAY;
+ s->sane_params.bytes_per_line = (s->sane_params.pixels_per_line + 7) / 8;
+ s->sane_params.depth = 1;
+ } */
+/*else*/ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0))
+ {
+ s->sane_params.format = SANE_FRAME_GRAY;
+ s->sane_params.bytes_per_line = s->sane_params.pixels_per_line;
+ s->sane_params.depth = 8;
+ } /*grey frame */
+ else
+ {
+ s->sane_params.format = SANE_FRAME_RGB;
+ s->sane_params.bytes_per_line = 3 * s->sane_params.pixels_per_line;
+ s->sane_params.depth = 8;
+ } /*color frame */
+ s->bytes_to_read = s->sane_params.lines * s->sane_params.bytes_per_line;
+ s->sane_params.last_frame = SANE_TRUE;
+ } /*!scanning */
+
+ if (params)
+ *params = s->sane_params;
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ AS6E_Scan *s = handle;
+ SANE_Status status = 0;
+ SANE_Word cap;
+ DBG (2, "sane_control_option\n");
+ if (info)
+ *info = 0;
+ if (s->scanning)
+ return SANE_STATUS_DEVICE_BUSY;
+ if (option >= NUM_OPTIONS)
+ return SANE_STATUS_INVAL;
+ cap = s->options_list[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ return SANE_STATUS_INVAL;
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ DBG (1, "sane_control_option %d, get value\n", option);
+ switch (option)
+ {
+ /* word options: */
+ case OPT_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ *(SANE_Word *) val = s->value[option].w;
+ return (SANE_STATUS_GOOD);
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->value[option].s);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ DBG (1, "sane_control_option %d, set value\n", option);
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ return (SANE_STATUS_INVAL);
+/* status = sanei_constrain_value (s->options_list[option], val, info);*/
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ if (info && s->value[option].w != *(SANE_Word *) val)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ /* fall through */
+ case OPT_NUM_OPTS:
+ case OPT_CONTRAST:
+ case OPT_BRIGHTNESS:
+ s->value[option].w = *(SANE_Word *) val;
+ DBG (1, "set brightness to\n");
+ return (SANE_STATUS_GOOD);
+ case OPT_MODE:
+ if (s->value[option].s)
+ free (s->value[option].s);
+ s->value[option].s = strdup (val);
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ return (SANE_STATUS_INVAL);
+}
+
+/*--------------------------------------------------------------------------*/
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ AS6E_Scan *s = handle;
+ DBG (2, "sane_get_option_descriptor\n");
+ if ((unsigned) option >= NUM_OPTIONS)
+ return (0);
+ return (&s->options_list[option]);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void
+sane_close (SANE_Handle handle)
+{
+ AS6E_Scan *prev, *s;
+ SANE_Word repeat = 0;
+ DBG (2, "sane_close\n");
+ /* remove handle from list of open handles: */
+ prev = 0;
+ for (s = first_handle; s; s = s->next)
+ {
+ if (s == handle)
+ break;
+ prev = s;
+ }
+ if (!s)
+ {
+ DBG (1, "close: invalid handle %p\n", handle);
+ return; /* oops, not a handle we know about */
+ }
+
+ if (s->scanning)
+ sane_cancel (handle);
+ write (s->as6e_params.ctloutpipe, &repeat, sizeof (repeat));
+ close (s->as6e_params.ctloutpipe);
+ free (s->scan_buffer);
+ free (s->line_buffer);
+ if (prev)
+ prev->next = s->next;
+ else
+ first_handle = s;
+ free (handle);
+}
+
+/*--------------------------------------------------------------------------*/
+void
+sane_exit (void)
+{
+ AS6E_Device *next;
+ DBG (2, "sane_exit\n");
+ while (first_dev != NULL)
+ {
+ next = first_dev->next;
+ free (first_dev);
+ first_dev = next;
+ }
+ if (devlist)
+ free (devlist);
+}
+
+/*--------------------------------------------------------------------------*/
+static SANE_Status
+as6e_open (AS6E_Scan * s)
+{
+
+ int data_processed, exec_result, as6e_status;
+ int ctloutpipe[2], ctlinpipe[2], datapipe[2];
+ char inpipe_desc[32], outpipe_desc[32], datapipe_desc[32];
+ pid_t fork_result;
+ DBG (1, "as6e_open\n");
+ memset (inpipe_desc, '\0', sizeof (inpipe_desc));
+ memset (outpipe_desc, '\0', sizeof (outpipe_desc));
+ memset (datapipe_desc, '\0', sizeof (datapipe_desc));
+ if ((pipe (ctloutpipe) == 0) && (pipe (ctlinpipe) == 0)
+ && (pipe (datapipe) == 0))
+ {
+ fork_result = fork ();
+ if (fork_result == (pid_t) - 1)
+ {
+ DBG (1, "Fork failure");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (fork_result == 0)
+ { /*in child */
+ sprintf (inpipe_desc, "%d", ctlinpipe[WRITEPIPE]);
+ sprintf (outpipe_desc, "%d", ctloutpipe[READPIPE]);
+ sprintf (datapipe_desc, "%d", datapipe[WRITEPIPE]);
+ exec_result =
+ execlp ("as6edriver", "as6edriver", "-s", inpipe_desc,
+ outpipe_desc, datapipe_desc, (char *) 0);
+ DBG (1, "The SANE backend was unable to start \"as6edriver\".\n");
+ DBG (1, "This must be installed in a driectory in your PATH.\n");
+ DBG (1, "To aquire the as6edriver program,\n");
+ DBG (1, "go to http://as6edriver.sourceforge.net.\n");
+ write (ctlinpipe[WRITEPIPE], &exec_result, sizeof (exec_result));
+ exit (-1);
+ }
+ else
+ { /*parent process */
+ data_processed =
+ read (ctlinpipe[READPIPE], &as6e_status, sizeof (as6e_status));
+ DBG (1, "%d - read %d status = %d\n", getpid (), data_processed,
+ as6e_status);
+ if (as6e_status == -2)
+ {
+ DBG (1, "Port access denied.\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+ if (as6e_status == -1)
+ {
+ DBG (1, "Could not contact scanner.\n");
+ return (SANE_STATUS_IO_ERROR);
+ }
+
+ if (as6e_status == 1)
+ DBG (1, "Using nibble mode.\n");
+ if (as6e_status == 2)
+ DBG (1, "Using byte mode.\n");
+ if (as6e_status == 3)
+ DBG (1, "Using EPP mode.\n");
+ s->as6e_params.ctlinpipe = ctlinpipe[READPIPE];
+ s->as6e_params.ctloutpipe = ctloutpipe[WRITEPIPE];
+ s->as6e_params.datapipe = datapipe[READPIPE];
+ s->child_pid = fork_result;
+ return (SANE_STATUS_GOOD);
+ } /*else */
+ }
+ else
+ return (SANE_STATUS_IO_ERROR);
+}
+
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp = NULL;
+
+ DBG_INIT ();
+ DBG (2, "sane_init (authorize %s null)\n", (authorize) ? "!=" : "==");
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+/* fp = sanei_config_open (AS6E_CONFIG_FILE);*/
+ if (!fp)
+ {
+ return (attach ("as6edriver", 0));
+ }
+
+ while (fgets (dev_name, sizeof (dev_name), fp))
+ {
+ if (dev_name[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (dev_name);
+ if (dev_name[len - 1] == '\n')
+ dev_name[--len] = '\0';
+ if (!len)
+ continue; /* ignore empty lines */
+/* sanei_config_attach_matching_devices (dev_name, attach_one);*/
+ }
+ fclose (fp);
+ return (SANE_STATUS_GOOD);
+}
+
+/*--------------------------------------------------------------------------*/
+/*
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (2, "attach_one\n");
+ attach (dev, 0);
+ return (SANE_STATUS_GOOD);
+}
+ */
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ static const SANE_Device **devlist = 0;
+ AS6E_Device *dev;
+ int i;
+ DBG (3, "sane_get_devices (local_only = %d)\n", local_only);
+ if (devlist)
+ free (devlist);
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+ *device_list = devlist;
+ return (SANE_STATUS_GOOD);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+static size_t
+max_string_size (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);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void
+initialize_options_list (AS6E_Scan * s)
+{
+
+ SANE_Int option;
+ DBG (2, "initialize_options_list\n");
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->options_list[option].size = sizeof (SANE_Word);
+ s->options_list[option].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ s->options_list[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->options_list[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->options_list[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_NUM_OPTS].size = sizeof (SANE_Word);
+ s->options_list[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->options_list[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ s->value[OPT_NUM_OPTS].w = NUM_OPTIONS;
+ s->options_list[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->options_list[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->options_list[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->options_list[OPT_MODE].type = SANE_TYPE_STRING;
+ s->options_list[OPT_MODE].size = max_string_size (mode_list);
+ s->options_list[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->options_list[OPT_MODE].constraint.string_list = mode_list;
+ s->value[OPT_MODE].s = strdup (mode_list[2]);
+ s->options_list[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->options_list[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->options_list[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->options_list[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->options_list[OPT_RESOLUTION].constraint.word_list = resolution_list;
+ s->value[OPT_RESOLUTION].w = 200;
+ s->options_list[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->options_list[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->options_list[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->options_list[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->options_list[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_TL_X].constraint.range = &x_range;
+ s->value[OPT_TL_X].w = s->options_list[OPT_TL_X].constraint.range->min;
+ s->options_list[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->options_list[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->options_list[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_TL_Y].constraint.range = &y_range;
+ s->value[OPT_TL_Y].w = s->options_list[OPT_TL_Y].constraint.range->min;
+ s->options_list[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->options_list[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->options_list[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->options_list[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->options_list[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BR_X].constraint.range = &x_range;
+ s->value[OPT_BR_X].w = s->options_list[OPT_BR_X].constraint.range->max;
+ s->options_list[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->options_list[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->options_list[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->options_list[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BR_Y].constraint.range = &y_range;
+ s->value[OPT_BR_Y].w = s->options_list[OPT_BR_Y].constraint.range->max;
+ s->options_list[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ s->options_list[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ s->options_list[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ s->options_list[OPT_CONTRAST].type = SANE_TYPE_INT;
+ s->options_list[OPT_CONTRAST].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_CONTRAST].constraint.range = &brightness_range;
+ s->value[OPT_BRIGHTNESS].w = 10;
+ s->options_list[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ s->options_list[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ s->options_list[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ s->options_list[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->options_list[OPT_BRIGHTNESS].constraint.range = &contrast_range;
+ s->value[OPT_CONTRAST].w = -32;
+}
+
+/*--------------------------------------------------------------------------*/
+static int
+check_for_driver (const char *devname)
+{
+#define NAMESIZE 128
+ struct stat statbuf;
+ mode_t modes;
+ char *path;
+ char fullname[NAMESIZE];
+ char dir[NAMESIZE];
+ int count = 0, offset = 0;
+
+ path = getenv ("PATH");
+ if (!path)
+ return 0;
+ while (path[count] != '\0')
+ {
+ memset (fullname, '\0', sizeof (fullname));
+ memset (dir, '\0', sizeof (dir));
+ while ((path[count] != ':') && (path[count] != '\0'))
+ {
+ dir[count - offset] = path[count];
+ count++;
+ }
+ /* use sizeof(fullname)-1 to make sure there is at least one padded null byte */
+ strncpy (fullname, dir, sizeof(fullname)-1);
+ /* take into account that fullname already contains non-null bytes */
+ strncat (fullname, "/", sizeof(fullname)-strlen(fullname)-1);
+ strncat (fullname, devname, sizeof(fullname)-strlen(fullname)-1);
+ if (!stat (fullname, &statbuf))
+ {
+ modes = statbuf.st_mode;
+ if (S_ISREG (modes))
+ return (1); /* found as6edriver */
+ }
+ if (path[count] == '\0')
+ return (0); /* end of path --no driver found */
+ count++;
+ offset = count;
+ }
+ return (0);
+}
+
+
+/*--------------------------------------------------------------------------*/
+static SANE_Status
+attach (const char *devname, AS6E_Device ** devp)
+{
+
+ AS6E_Device *dev;
+
+/* SANE_Status status; */
+ DBG (2, "attach\n");
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devname) == 0)
+ {
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ dev = malloc (sizeof (*dev));
+ if (!dev)
+ return (SANE_STATUS_NO_MEM);
+ memset (dev, 0, sizeof (*dev));
+ dev->sane.name = strdup (devname);
+ if (!check_for_driver (devname))
+ {
+ free (dev);
+ return (SANE_STATUS_INVAL);
+ }
+
+ dev->sane.model = "AS6E";
+ dev->sane.vendor = "Artec";
+ dev->sane.type = "flatbed scanner";
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+ if (devp)
+ *devp = dev;
+ return (SANE_STATUS_GOOD);
+}
+
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ SANE_Status status;
+ AS6E_Device *dev;
+ AS6E_Scan *s;
+ DBG (2, "sane_open\n");
+ if (devicename[0])
+ {
+ for (dev = first_dev; dev; dev = dev->next)
+ if (strcmp (dev->sane.name, devicename) == 0)
+ break;
+ if (!dev)
+ {
+ status = attach (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
+ }
+ }
+ else
+ {
+ /* empty devicname -> use first device */
+ dev = first_dev;
+ }
+ if (!dev)
+ return SANE_STATUS_INVAL;
+ s = malloc (sizeof (*s));
+ if (!s)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+ s->scan_buffer = malloc (SCAN_BUF_SIZE);
+ if (!s->scan_buffer)
+ return SANE_STATUS_NO_MEM;
+ memset (s->scan_buffer, 0, SCAN_BUF_SIZE);
+ s->line_buffer = malloc (SCAN_BUF_SIZE);
+ if (!s->line_buffer)
+ return SANE_STATUS_NO_MEM;
+ memset (s->line_buffer, 0, SCAN_BUF_SIZE);
+ status = as6e_open (s);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ initialize_options_list (s);
+ s->scanning = 0;
+ /* insert newly opened handle into list of open handles: */
+ s->next = first_handle;
+ first_handle = s;
+ *handle = s;
+ return (status);
+}
+
+/*--------------------------------------------------------------------------*/
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (2, "sane_set_io_mode( %p, %d )\n", handle, non_blocking);
+ return (SANE_STATUS_UNSUPPORTED);
+}
+
+/*---------------------------------------------------------------------------*/
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd( %p, %p )\n",(void *) handle, (void *) fd);
+ return SANE_STATUS_UNSUPPORTED;
+}