kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			3885 wiersze
		
	
	
		
			112 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			3885 wiersze
		
	
	
		
			112 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
|    Copyright (C) 1999,2000 Tom Martone
 | |
|    This file is part of a SANE backend for Bell and Howell Copiscan II
 | |
|    Scanners using the Remote SCSI Controller(RSC).
 | |
| 
 | |
|    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.
 | |
| 
 | |
| */
 | |
| #include "../include/sane/config.h"
 | |
| #include <limits.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <ctype.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/types.h>
 | |
| #include "../include/sane/sane.h"
 | |
| #include "../include/sane/saneopts.h"
 | |
| #include "../include/sane/sanei_scsi.h"
 | |
| #include "../include/sane/sanei_config.h"
 | |
| 
 | |
| #define BACKEND_NAME bh
 | |
| #include "../include/sane/sanei_backend.h"
 | |
| #define BUILD 4
 | |
| 
 | |
| #include "bh.h"
 | |
| 
 | |
| #define MIN(x,y) ((x)<(y) ? (x) : (y))
 | |
| #define MAX(x,y) ((x)>(y) ? (x) : (y))
 | |
| 
 | |
| static const SANE_Device **devlist = 0;
 | |
| static int num_devices = 0;
 | |
| static BH_Device *first_dev = NULL;
 | |
| static BH_Scanner *first_handle = NULL;
 | |
| static SANE_Char inquiry_data[255] = "Bell+Howell scanner";
 | |
| static SANE_Int disable_optional_frames = 0;
 | |
| static SANE_Int fake_inquiry = 0;
 | |
| 
 | |
| static int allblank(const char *s)
 | |
| {
 | |
|   while (s && *s)
 | |
|     if (!isspace(*s++))
 | |
|       return 0;
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 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
 | |
| trim_spaces(char *s, size_t n)
 | |
| {
 | |
|   for (s += (n-1); n > 0; n--, s--)
 | |
|     {
 | |
|       if (*s && !isspace(*s))
 | |
| 	break;
 | |
|       *s = '\0';
 | |
|     }
 | |
| }
 | |
| 
 | |
| static SANE_String_Const
 | |
| print_devtype (SANE_Byte devtype)
 | |
| {
 | |
|   static SANE_String devtypes[] =
 | |
|   {
 | |
|     "disk",
 | |
|     "tape",
 | |
|     "printer",
 | |
|     "processor",
 | |
|     "CD-writer",
 | |
|     "CD-drive",
 | |
|     "scanner",
 | |
|     "optical-drive",
 | |
|     "jukebox",
 | |
|     "communicator"
 | |
|   };
 | |
| 
 | |
|   return (devtype > 0 && devtype < NELEMS(devtypes)) ?
 | |
|     devtypes[devtype] :
 | |
|     "unknown-device";
 | |
| }
 | |
| 
 | |
| static SANE_String_Const
 | |
| print_barcodetype (SANE_Int i)
 | |
| {
 | |
|   return (i > 0 && i < NELEMS(barcode_search_bar_list)) ?
 | |
|     barcode_search_bar_list[i] :
 | |
|     (SANE_String_Const) "unknown";
 | |
| }
 | |
| 
 | |
| static SANE_String_Const
 | |
| print_orientation (SANE_Int i)
 | |
| {
 | |
|   switch(i)
 | |
|     {
 | |
|     case 0:
 | |
|     case 7:
 | |
|       return "vertical upwards";
 | |
|     case 1:
 | |
|     case 2:
 | |
|       return "horizontal right";
 | |
|     case 3:
 | |
|     case 4:
 | |
|       return "vertical downwards";
 | |
|     case 5:
 | |
|     case 6:
 | |
|       return "horizontal left";
 | |
|     default:
 | |
|       return "unknown";
 | |
|     }
 | |
| }
 | |
| 
 | |
| static SANE_String_Const
 | |
| print_read_type (SANE_Int i)
 | |
| {
 | |
|   static char buf[32];
 | |
|   SANE_Int n;
 | |
| 
 | |
|   /* translate BH_SCSI_READ_TYPE_ codes to a human-readable string */
 | |
|   if (i == BH_SCSI_READ_TYPE_FRONT)
 | |
|     {
 | |
|       strcpy(buf, "front page");
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_BACK)
 | |
|     {
 | |
|       strcpy(buf, "back page");
 | |
|     }
 | |
|   else if (i > BH_SCSI_READ_TYPE_FRONT &&
 | |
| 	   i <= BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS)
 | |
|     {
 | |
|       n = i - BH_SCSI_READ_TYPE_FRONT;
 | |
|       sprintf(buf, "front section %d", n);
 | |
|     }
 | |
|   else if (i > BH_SCSI_READ_TYPE_BACK &&
 | |
| 	   i <= BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS)
 | |
|     {
 | |
|       n = i - BH_SCSI_READ_TYPE_BACK;
 | |
|       sprintf(buf, "back section %d", n);
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_FRONT_BARCODE)
 | |
|     {
 | |
|       strcpy(buf, "front page barcode");
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_BACK_BARCODE)
 | |
|     {
 | |
|       strcpy(buf, "back page barcode");
 | |
|     }
 | |
|   else if (i > BH_SCSI_READ_TYPE_FRONT_BARCODE &&
 | |
| 	   i <= BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)
 | |
|     {
 | |
|       n = i - BH_SCSI_READ_TYPE_FRONT_BARCODE;
 | |
|       sprintf(buf, "front barcode section %d", n);
 | |
|     }
 | |
|   else if (i > BH_SCSI_READ_TYPE_BACK_BARCODE &&
 | |
| 	   i <= BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)
 | |
|     {
 | |
|       n = i - BH_SCSI_READ_TYPE_BACK_BARCODE;
 | |
|       sprintf(buf, "back barcode section %d", n);
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_FRONT_PATCHCODE)
 | |
|     {
 | |
|       strcpy(buf, "front page patchcode");
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_BACK_PATCHCODE)
 | |
|     {
 | |
|       strcpy(buf, "back page patchcode");
 | |
|     }
 | |
|   else if (i > BH_SCSI_READ_TYPE_FRONT_PATCHCODE &&
 | |
| 	   i <= BH_SCSI_READ_TYPE_FRONT_PATCHCODE + NUM_SECTIONS)
 | |
|     {
 | |
|       n = i - BH_SCSI_READ_TYPE_FRONT_PATCHCODE;
 | |
|       sprintf(buf, "front patchcode section %d", n);
 | |
|     }
 | |
|   else if (i > BH_SCSI_READ_TYPE_BACK_PATCHCODE &&
 | |
| 	   i <= BH_SCSI_READ_TYPE_BACK_PATCHCODE + NUM_SECTIONS)
 | |
|     {
 | |
|       n = i - BH_SCSI_READ_TYPE_BACK_PATCHCODE;
 | |
|       sprintf(buf, "back patchcode section %d", n);
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_FRONT_ICON)
 | |
|     {
 | |
|       strcpy(buf, "front page icon");
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_BACK_ICON)
 | |
|     {
 | |
|       strcpy(buf, "back page icon");
 | |
|     }
 | |
|   else if (i == BH_SCSI_READ_TYPE_SENDBARFILE)
 | |
|     {
 | |
|       strcpy(buf, "transmit bar/patch codes");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       strcpy(buf, "unknown");
 | |
|     }
 | |
| 
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| static SANE_Int
 | |
| get_rotation_id(char *s)
 | |
| {
 | |
|   SANE_Int i;
 | |
| 
 | |
|   for (i = 0; rotation_list[i]; i++)
 | |
|     if (strcmp(s, rotation_list[i]) == 0)
 | |
|       break;
 | |
| 
 | |
|   /* unknown strings are treated as '0' */
 | |
|   return rotation_list[i] ? i : 0;
 | |
| }
 | |
| 
 | |
| static SANE_Int
 | |
| get_compression_id(char *s)
 | |
| {
 | |
|   SANE_Int i;
 | |
| 
 | |
|   for (i = 0; compression_list[i]; i++)
 | |
|     if (strcmp(s, compression_list[i]) == 0)
 | |
|       break;
 | |
| 
 | |
|   /* unknown strings are treated as 'none' */
 | |
|   return compression_list[i] ?  i : 0;
 | |
| }
 | |
| 
 | |
| static SANE_Int
 | |
| get_barcode_id(char *s)
 | |
| {
 | |
|   SANE_Int i;
 | |
| 
 | |
|   for (i = 0; barcode_search_bar_list[i]; i++)
 | |
|     if (strcmp(s, barcode_search_bar_list[i]) == 0)
 | |
|       break;
 | |
| 
 | |
|   /* unknown strings are treated as 'none' */
 | |
|   return barcode_search_bar_list[i] ?  i : 0;
 | |
| }
 | |
| 
 | |
| static SANE_Int
 | |
| get_scan_mode_id(char *s)
 | |
| {
 | |
|   SANE_Int i;
 | |
| 
 | |
|   for (i = 0; scan_mode_list[i]; i++)
 | |
|     if (strcmp(s, scan_mode_list[i]) == 0)
 | |
|       break;
 | |
| 
 | |
|   /* unknown strings are treated as 'lineart' */
 | |
|   return scan_mode_list[i] ?  i : 0;
 | |
| }
 | |
| 
 | |
| static SANE_Int
 | |
| get_paper_id(char *s)
 | |
| {
 | |
|   SANE_Int i;
 | |
| 
 | |
|   for (i = 0; paper_list[i]; i++)
 | |
|     if (strcmp(s, paper_list[i]) == 0)
 | |
|       break;
 | |
| 
 | |
|   /* unknown strings are treated as 'custom' */
 | |
|   return paper_list[i] ?  i : 0;
 | |
| }
 | |
| 
 | |
| static SANE_Int
 | |
| get_barcode_search_mode(char *s)
 | |
| {
 | |
|   SANE_Int i;
 | |
| 
 | |
|   if (strcmp(s, "horizontal") == 0)
 | |
|     {
 | |
|       i = 1;
 | |
|     }
 | |
|   else if (strcmp(s, "vertical") == 0)
 | |
|     {
 | |
|       i = 2;
 | |
|     }
 | |
|   else if (strcmp(s, "vert-horiz") == 0)
 | |
|     {
 | |
|       i = 6;
 | |
|     }
 | |
|   else if (strcmp(s, "horiz-vert") == 0)
 | |
|     {
 | |
|       i = 9;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* unknown strings are treated as 'horiz-vert' */
 | |
|       DBG(1, "get_barcode_search_mode: unrecognized string `%s'\n", s);
 | |
|       i = 9;
 | |
|     }
 | |
| 
 | |
|   return i;
 | |
| }
 | |
| 
 | |
| static void
 | |
| appendStdList(BH_Info *sc, SANE_Int res)
 | |
| {
 | |
|   /*  append entry to resolution list - a SANE_WORD_LIST */
 | |
|   sc->resStdList[sc->resStdList[0]+1] = res;
 | |
|   sc->resStdList[0]++;
 | |
| }
 | |
| 
 | |
| static void
 | |
| ScannerDump(BH_Scanner *s)
 | |
| {
 | |
|   int i;
 | |
|   BH_Info *info;
 | |
|   SANE_Device *sdev;
 | |
| 
 | |
|   info = &s->hw->info;
 | |
|   sdev = &s->hw->sane;
 | |
| 
 | |
|   DBG (1, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n",
 | |
| 	 sdev->name,
 | |
| 	 sdev->vendor,
 | |
| 	 sdev->model,
 | |
| 	 sdev->type);
 | |
| 
 | |
|   DBG (1, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n",
 | |
| 	 print_devtype(info->devtype),
 | |
| 	 info->vendor,
 | |
| 	 info->product,
 | |
| 	 info->revision);
 | |
| 
 | |
|   DBG (1, "Automatic Document Feeder:%s\n",
 | |
| 	 info->canADF ? " <Installed>" : " <Not Installed>");
 | |
| 
 | |
|   DBG (1, "Colors:%s%s\n", info->colorBandW ? " <Black and White>" : "",
 | |
| 	 info->colorHalftone ? " <Halftone>" : "");
 | |
| 
 | |
|   DBG (1, "Data processing:%s%s%s%s%s%s\n",
 | |
| 	 info->canWhiteFrame ? " <White Frame>" : "",
 | |
| 	 info->canBlackFrame ? " <Black Frame>" : "",
 | |
| 	 info->canEdgeExtract ? " <Edge Extraction>" : "",
 | |
| 	 info->canNoiseFilter ? " <Noise Filter>" : "",
 | |
| 	 info->canSmooth ? " <Smooth>" : "",
 | |
| 	 info->canLineBold ? " <Line Bolding>" : "");
 | |
| 
 | |
|   DBG (1, "Compression:%s%s%s\n",
 | |
| 	 info->comprG3_1D ? " <Group 3, 1D>" : "",
 | |
| 	 info->comprG3_2D ? " <Group 3, 2D>" : "",
 | |
| 	 info->comprG4 ? " <Group 4>" : "");
 | |
| 
 | |
|   DBG (1, "Optional Features:%s%s%s%s\n",
 | |
| 	 info->canBorderRecog ? " <Border Recognition>" : "",
 | |
| 	 info->canBarCode ? " <BarCode Decoding>" : "",
 | |
| 	 info->canIcon ? " <Icon Generation>" : "",
 | |
| 	 info->canSection ? " <Section Support>" : "");
 | |
| 
 | |
|   DBG (1, "Max bytes per scan-line: %d (%d pixels)\n",
 | |
| 	 info->lineMaxBytes,
 | |
| 	 info->lineMaxBytes * 8);
 | |
| 
 | |
|   DBG (1, "Basic resolution (X/Y): %d/%d\n",
 | |
| 	 info->resBasicX,
 | |
| 	 info->resBasicY);
 | |
| 
 | |
|   DBG (1, "Maximum resolution (X/Y): %d/%d\n",
 | |
| 	 info->resMaxX,
 | |
| 	 info->resMaxY);
 | |
| 
 | |
|   DBG (1, "Minimum resolution (X/Y): %d/%d\n",
 | |
| 	 info->resMinX,
 | |
| 	 info->resMinY);
 | |
| 
 | |
|   DBG (1, "Standard Resolutions:\n");
 | |
|   for (i = 0; i < info->resStdList[0]; i++)
 | |
|     DBG (1, " %d\n", info->resStdList[i+1]);
 | |
| 
 | |
|   DBG (1, "Window Width/Height (in basic res) %d/%d (%.2f/%.2f inches)\n",
 | |
| 	 info->winWidth,
 | |
| 	 info->winHeight,
 | |
| 	 (info->resBasicX != 0) ? ((float) info->winWidth) / info->resBasicX : 0.0,
 | |
| 	 (info->resBasicY) ? ((float) info->winHeight) / info->resBasicY : 0.0);
 | |
| 
 | |
|   DBG (1, "Summary:%s%s%s\n",
 | |
| 	 info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
 | |
| 	 info->canACE ? " (ACE capable)" : "",
 | |
| 	 info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
 | |
| 
 | |
|   sprintf(inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s%s\n",
 | |
| 	 info->vendor,
 | |
| 	 info->product,
 | |
| 	 info->revision,
 | |
| 	 info->canDuplex ? "Duplex Scanner" : "Simplex Scanner",
 | |
| 	 info->canACE ? " (ACE capable)" : "",
 | |
| 	 info->canCheckADF ? " (ADF Paper Sensor capable)" : "");
 | |
| 
 | |
|   DBG (5, "autoborder_default=%d\n", info->autoborder_default);
 | |
|   DBG (5, "batch_default=%d\n", info->batch_default);
 | |
|   DBG (5, "deskew_default=%d\n", info->deskew_default);
 | |
|   DBG (5, "check_adf_default=%d\n", info->check_adf_default);
 | |
|   DBG (5, "duplex_default=%d\n", info->duplex_default);
 | |
|   DBG (5, "timeout_adf_default=%d\n", info->timeout_adf_default);
 | |
|   DBG (5, "timeout_manual_default=%d\n", info->timeout_manual_default);
 | |
|   DBG (5, "control_panel_default=%d\n", info->control_panel_default);
 | |
| 
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| test_unit_ready (int fd)
 | |
| {
 | |
|   static SANE_Byte cmd[6];
 | |
|   SANE_Status status;
 | |
|   DBG (3, "test_unit_ready called\n");
 | |
| 
 | |
|   cmd[0] = BH_SCSI_TEST_UNIT_READY;
 | |
|   memset (cmd, 0, sizeof (cmd));
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| object_position (BH_Scanner *s)
 | |
| {
 | |
|   static SANE_Byte cmd[10];
 | |
|   SANE_Status status;
 | |
|   DBG (3, "object_position called\n");
 | |
| 
 | |
|   memset (cmd, 0, sizeof (cmd));
 | |
|   cmd[0] = BH_SCSI_OBJECT_POSITION;
 | |
|   cmd[1] = 0x01;
 | |
|   status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_barcode_data (BH_Scanner *s, FILE *fp)
 | |
| {
 | |
|   static SANE_Byte cmd[10];
 | |
|   SANE_Status status;
 | |
|   SANE_Int num_found = 0;
 | |
|   double w, l, x, y, res;
 | |
|   struct barcode_data buf;
 | |
|   size_t buf_size = sizeof(buf);
 | |
|   DBG (3, "read_barcode_data called\n");
 | |
| 
 | |
|   memset (&cmd, 0, sizeof (cmd));
 | |
|   cmd[0] = BH_SCSI_READ_SCANNED_DATA;
 | |
|   cmd[2] = s->readlist[s->readptr];
 | |
|   _lto3b(buf_size, &cmd[6]); /* transfer length */
 | |
| 
 | |
|   s->barcode_not_found = SANE_FALSE;
 | |
|   do {
 | |
|     memset (&buf, 0, sizeof(buf));
 | |
|     status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size);
 | |
|     if (status != SANE_STATUS_GOOD)
 | |
|       break;
 | |
|     if (s->barcode_not_found == SANE_TRUE)
 | |
|       break;
 | |
| 
 | |
|     num_found++;
 | |
| 
 | |
|     buf.barcodedata[sizeof(buf.barcodedata)-1] = '\0';
 | |
| 
 | |
|     /* calculate the bounding rectangle */
 | |
|     x = MIN((int) _2btol(buf.posxb), (int) _2btol(buf.posxa));
 | |
|     y = MIN((int) _2btol(buf.posyb), (int) _2btol(buf.posyd));
 | |
|     w = MAX((int) _2btol(buf.posxd), (int) _2btol(buf.posxd)) - x;
 | |
|     l = MAX((int) _2btol(buf.posya), (int) _2btol(buf.posyc)) - y;
 | |
|     /* convert from pixels to mm */
 | |
|     res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
 | |
|     if (res <= 0.0)
 | |
|       {
 | |
| 	/* avoid divide by zero */
 | |
| 	DBG(1, "read_barcode_data: warning: "
 | |
| 	    "encountered bad resolution value '%f', replacing with '%f'\n",
 | |
| 	    res, 200.0);
 | |
| 	res = 200.0;
 | |
|       }
 | |
|     x = x * MM_PER_INCH / res;
 | |
|     y = y * MM_PER_INCH / res;
 | |
|     w = w * MM_PER_INCH / res;
 | |
|     l = l * MM_PER_INCH / res;
 | |
|     /* add a bit of a border around the edges */
 | |
|     x = MAX(0.0, x - BH_DECODE_FUDGE);
 | |
|     y = MAX(0.0, y - BH_DECODE_FUDGE);
 | |
|     w += (BH_DECODE_FUDGE * 4);
 | |
|     l += (BH_DECODE_FUDGE * 4);
 | |
| 
 | |
|     /* write the decoded barcode data into the file */
 | |
|     fprintf(fp, "<barcode>\n <section>%s</section>\n",
 | |
| 	    print_read_type((int) s->readlist[s->readptr]));
 | |
|     fprintf(fp, " <type>%s</type>\n <status-flag>%d</status-flag>\n",
 | |
| 	    print_barcodetype((int) _2btol(buf.barcodetype)),
 | |
| 	    (int) _2btol(buf.statusflag));
 | |
|     fprintf(fp, " <orientation>%s</orientation>\n",
 | |
| 	    print_orientation((int) _2btol(buf.barcodeorientation)));
 | |
|     fprintf(fp, " <location>\n  <tl><x>%d</x><y>%d</y></tl>\n",
 | |
| 	    (int) _2btol(buf.posxb), (int) _2btol(buf.posyb));
 | |
|     fprintf(fp, "  <tr><x>%d</x><y>%d</y></tr>\n",
 | |
| 	    (int) _2btol(buf.posxd), (int) _2btol(buf.posyd));
 | |
|     fprintf(fp, "  <bl><x>%d</x><y>%d</y></bl>\n",
 | |
| 	    (int) _2btol(buf.posxa), (int) _2btol(buf.posya));
 | |
|     fprintf(fp, "  <br><x>%d</x><y>%d</y></br>\n </location>\n",
 | |
| 	    (int) _2btol(buf.posxc), (int) _2btol(buf.posyc));
 | |
|     fprintf(fp, " <rectangle>%.2fx%.2f+%.2f+%.2f</rectangle>\n",
 | |
| 	    w, l, x, y);
 | |
|     fprintf(fp, " <search-time>%d</search-time>\n <length>%d</length>\n",
 | |
| 	    (int) _2btol(buf.barcodesearchtime),
 | |
| 	    (int) buf.barcodelen);
 | |
|     fprintf(fp, " <data>%s</data>\n</barcode>\n",
 | |
| 	    buf.barcodedata);
 | |
|   } while (num_found <= BH_DECODE_TRIES);
 | |
| 
 | |
|   DBG (3, "read_barcode_data: found %d barcodes, returning %s\n",
 | |
|        num_found, sane_strstatus(status));
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_icon_data (BH_Scanner *s)
 | |
| {
 | |
|   static SANE_Byte cmd[10];
 | |
|   SANE_Status status;
 | |
|   struct icon_data buf;
 | |
|   size_t buf_size = sizeof(buf);
 | |
|   DBG (3, "read_icon_data called\n");
 | |
| 
 | |
|   memset (&cmd, 0, sizeof (cmd));
 | |
|   cmd[0] = BH_SCSI_READ_SCANNED_DATA;
 | |
|   cmd[2] = s->readlist[s->readptr];
 | |
|   _lto3b(buf_size, &cmd[6]); /* transfer length */
 | |
| 
 | |
|   memset (&buf, 0, sizeof(buf));
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size);
 | |
| 
 | |
|   /* set the fields in the scanner handle for later reference */
 | |
|   s->iconwidth = _4btol(buf.iconwidth);
 | |
|   s->iconlength = _4btol(buf.iconlength);
 | |
| 
 | |
|   DBG(3, "read_icon_data: windowwidth:%lu, windowlength:%lu\n",
 | |
|       _4btol(buf.windowwidth),
 | |
|       _4btol(buf.windowlength));
 | |
|   DBG(3, "read_icon_data: iconwidth:%lu, iconlength:%lu, iconwidth(bytes):%lu\n",
 | |
|       _4btol(buf.iconwidth),
 | |
|       _4btol(buf.iconlength),
 | |
|       _4btol(buf.iconwidthbytes));
 | |
|   DBG(3, "read_icon_data: bitordering:%02x, icondatalen:%lu\n",
 | |
|       buf.bitordering,
 | |
|       _4btol(buf.icondatalen));
 | |
| 
 | |
|   DBG (3, "read_icon_data returning %d\n", status);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_barfile (BH_Scanner *s, void *buf, size_t *buf_size)
 | |
| {
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   size_t nread;
 | |
|   DBG (3, "read_barfile called (%lu bytes)\n", (u_long) *buf_size);
 | |
| 
 | |
|   if (s->barf != NULL)
 | |
|     {
 | |
|       /* this function needs to set InvalidBytes so it looks
 | |
|        * like a B&H scsi EOF
 | |
|        */
 | |
|       if ((nread = fread(buf, 1, *buf_size, s->barf)) < *buf_size)
 | |
| 	{
 | |
| 	  /* set InvalidBytes */
 | |
| 	  s->InvalidBytes = *buf_size - nread;
 | |
| 
 | |
| 	  if (ferror(s->barf))
 | |
| 	    {
 | |
| 	      status = SANE_STATUS_IO_ERROR;
 | |
| 	      fclose(s->barf);
 | |
| 	      s->barf = NULL;
 | |
| 	      unlink(s->barfname);
 | |
| 	    }
 | |
| 	  else if (feof(s->barf))
 | |
| 	    {
 | |
| 	      /* it also needs to close the file and delete it when EOF is
 | |
| 	       * reached.
 | |
| 	       */
 | |
| 	      fclose(s->barf);
 | |
| 	      s->barf = NULL;
 | |
| 	      unlink(s->barfname);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* set InvalidBytes */
 | |
|       s->InvalidBytes = *buf_size;
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_data (BH_Scanner *s, void *buf, size_t *buf_size)
 | |
| {
 | |
|   static SANE_Byte cmd[10];
 | |
|   SANE_Status status;
 | |
|   DBG (3, "read_data called (%lu bytes)\n", (u_long) *buf_size);
 | |
| 
 | |
|   if (s->readlist[s->readptr] == BH_SCSI_READ_TYPE_SENDBARFILE)
 | |
|     {
 | |
|       /* call special barcode data read function. */
 | |
|       status = read_barfile(s, buf, buf_size);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       memset (&cmd, 0, sizeof (cmd));
 | |
|       cmd[0] = BH_SCSI_READ_SCANNED_DATA;
 | |
|       cmd[2] = s->readlist[s->readptr];
 | |
|       _lto3b(*buf_size, &cmd[6]); /* transfer length */
 | |
| 
 | |
|       status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, buf_size);
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_measurement (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_03 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (3, "mode_select_measurement called (bmu:%d mud:%d)\n",
 | |
|        s->bmu, s->mud);
 | |
| 
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|   select_cmd.cmd[1] = 0x10;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   select_cmd.mp.pagecode = BH_MODE_MEASUREMENT_PAGE_CODE;
 | |
|   select_cmd.mp.paramlen = 0x06;
 | |
|   select_cmd.mp.bmu = s->bmu;
 | |
|   _lto2b(s->mud, select_cmd.mp.mud);
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_timeout (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_20 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (3, "mode_select_timeout called\n");
 | |
| 
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|   select_cmd.cmd[1] = 0x10;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   select_cmd.mp.pagecode = BH_MODE_TIMEOUT_PAGE_CODE;
 | |
|   select_cmd.mp.paramlen = 0x06;
 | |
|   select_cmd.mp.timeoutmanual = _OPT_VAL_WORD(s, OPT_TIMEOUT_MANUAL);
 | |
|   select_cmd.mp.timeoutadf = _OPT_VAL_WORD(s, OPT_TIMEOUT_ADF);
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_icon (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_21 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (3, "mode_select_icon called\n");
 | |
| 
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|   select_cmd.cmd[1] = 0x10;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   select_cmd.mp.pagecode = BH_MODE_ICON_PAGE_CODE;
 | |
|   select_cmd.mp.paramlen = 0x06;
 | |
|   _lto2b(_OPT_VAL_WORD(s, OPT_ICON_WIDTH), select_cmd.mp.iconwidth);
 | |
|   _lto2b(_OPT_VAL_WORD(s, OPT_ICON_LENGTH), select_cmd.mp.iconlength);
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_barcode_priority (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_30 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
|   int i;
 | |
| 
 | |
|   DBG (3, "mode_select_barcode_priority called\n");
 | |
| 
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|   select_cmd.cmd[1] = 0x10;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   select_cmd.mp.pagecode = BH_MODE_BARCODE_PRIORITY_PAGE_CODE;
 | |
|   select_cmd.mp.paramlen = 0x06;
 | |
| 
 | |
|   for (i = 0; i < NUM_SEARCH_BARS; i++)
 | |
|     {
 | |
|       /* anything after a 'none' is ignored */
 | |
|       if ((select_cmd.mp.priority[i] = s->search_bars[i]) == 0) break;
 | |
|     }
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_barcode_param1 (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_31 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (3, "mode_select_barcode_param1 called\n");
 | |
| 
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|   select_cmd.cmd[1] = 0x10;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM1_PAGE_CODE;
 | |
|   select_cmd.mp.paramlen = 0x06;
 | |
| 
 | |
|   _lto2b((SANE_Int)_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BARCODE_HMIN), select_cmd.mp.minbarheight);
 | |
|   select_cmd.mp.searchcount = _OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_COUNT);
 | |
|   select_cmd.mp.searchmode =
 | |
|     get_barcode_search_mode(_OPT_VAL_STRING(s, OPT_BARCODE_SEARCH_MODE));
 | |
|   _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_TIMEOUT), select_cmd.mp.searchtimeout);
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_barcode_param2 (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_32 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
|   size_t len;
 | |
| 
 | |
|   DBG (3, "mode_select_barcode_param2 called\n");
 | |
| 
 | |
|   /* first we'll do a mode sense, then we'll overwrite with
 | |
|    * our new values, and then do a mode select
 | |
|    */
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SENSE;
 | |
|   select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM2_PAGE_CODE;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   len = sizeof(select_cmd.mp);
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd),
 | |
| 			   &select_cmd.mp, &len);
 | |
| 
 | |
|   if (status == SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG(8, "mode_select_barcode_param2: sensed values: relmax:%d barmin:%d barmax:%d\n",
 | |
| 	  (int) _2btol(select_cmd.mp.relmax),
 | |
| 	  (int) _2btol(select_cmd.mp.barmin),
 | |
| 	  (int) _2btol(select_cmd.mp.barmax));
 | |
| 
 | |
|       memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd));
 | |
|       select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|       select_cmd.cmd[1] = 0x10;
 | |
|       select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|       select_cmd.mp.modedatalen = 0x00;
 | |
|       select_cmd.mp.mediumtype = 0x00;
 | |
|       select_cmd.mp.devicespecificparam = 0x00;
 | |
|       select_cmd.mp.blockdescriptorlen = 0x00;
 | |
| 
 | |
|       select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM2_PAGE_CODE;
 | |
|       select_cmd.mp.paramlen = 0x06;
 | |
| 
 | |
|       /* only overwrite the default values if the option is non-zero */
 | |
|       if (_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX) != 0)
 | |
| 	{
 | |
| 	  _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX), select_cmd.mp.relmax);
 | |
| 	}
 | |
|       if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN) != 0)
 | |
| 	{
 | |
| 	  _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN), select_cmd.mp.barmin);
 | |
| 	}
 | |
|       if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX) != 0)
 | |
| 	{
 | |
| 	  _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX), select_cmd.mp.barmax);
 | |
| 	}
 | |
| 
 | |
|       DBG(8, "mode_select_barcode_param2: param values: relmax:%d barmin:%d barmax:%d\n",
 | |
| 	  (int) _OPT_VAL_WORD(s, OPT_BARCODE_RELMAX),
 | |
| 	  (int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMIN),
 | |
| 	  (int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMAX));
 | |
| 
 | |
|       DBG(8, "mode_select_barcode_param2: select values: relmax:%d barmin:%d barmax:%d\n",
 | |
| 	  (int) _2btol(select_cmd.mp.relmax),
 | |
| 	  (int) _2btol(select_cmd.mp.barmin),
 | |
| 	  (int) _2btol(select_cmd.mp.barmax));
 | |
| 
 | |
|       status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_barcode_param3 (BH_Scanner *s)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[6];
 | |
|     struct mode_page_33 mp;
 | |
|   } select_cmd;
 | |
|   SANE_Status status;
 | |
|   size_t len;
 | |
| 
 | |
|   DBG (3, "mode_select_barcode_param3 called\n");
 | |
| 
 | |
|   /* first we'll do a mode sense, then we'll overwrite with
 | |
|    * our new values, and then do a mode select
 | |
|    */
 | |
|   memset (&select_cmd, 0, sizeof (select_cmd));
 | |
|   select_cmd.cmd[0] = BH_SCSI_MODE_SENSE;
 | |
|   select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM3_PAGE_CODE;
 | |
|   select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|   len = sizeof(select_cmd.mp);
 | |
|   status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd),
 | |
| 			   &select_cmd.mp, &len);
 | |
| 
 | |
|   if (status == SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG(8, "mode_select_barcode_param3: sensed values: contrast:%d patchmode:%d\n",
 | |
| 	  (int) _2btol(select_cmd.mp.barcodecontrast),
 | |
| 	  (int) _2btol(select_cmd.mp.patchmode));
 | |
| 
 | |
|       memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd));
 | |
|       select_cmd.cmd[0] = BH_SCSI_MODE_SELECT;
 | |
|       select_cmd.cmd[1] = 0x10;
 | |
|       select_cmd.cmd[4] = sizeof(select_cmd.mp);
 | |
| 
 | |
|       select_cmd.mp.modedatalen = 0x00;
 | |
|       select_cmd.mp.mediumtype = 0x00;
 | |
|       select_cmd.mp.devicespecificparam = 0x00;
 | |
|       select_cmd.mp.blockdescriptorlen = 0x00;
 | |
| 
 | |
|       select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM3_PAGE_CODE;
 | |
|       select_cmd.mp.paramlen = 0x06;
 | |
| 
 | |
|       /* only overwrite the default values if the option is non-zero */
 | |
|       if (_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST) != 0)
 | |
| 	{
 | |
| 	  _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST), select_cmd.mp.barcodecontrast);
 | |
| 	}
 | |
|       if (_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE) != 0)
 | |
| 	{
 | |
| 	  _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE), select_cmd.mp.patchmode);
 | |
| 	}
 | |
| 
 | |
|       DBG(8, "mode_select_barcode_param3: param values: contrast:%d patchmode:%d\n",
 | |
| 	  (int) _OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST),
 | |
| 	  (int) _OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE));
 | |
| 
 | |
|       DBG(8, "mode_select_barcode_param3: select values: contrast:%d patchmode:%d\n",
 | |
| 	  (int) _2btol(select_cmd.mp.barcodecontrast),
 | |
| 	  (int) _2btol(select_cmd.mp.patchmode));
 | |
| 
 | |
|       status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0);
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| inquiry (int fd, void *buf, size_t *buf_size, SANE_Byte evpd, SANE_Byte page_code)
 | |
| {
 | |
|   static SANE_Byte cmd[6];
 | |
|   SANE_Status status;
 | |
|   DBG (3, "inquiry called\n");
 | |
| 
 | |
|   memset (cmd, 0, sizeof (cmd));
 | |
|   cmd[0] = BH_SCSI_INQUIRY;
 | |
|   cmd[1] = evpd;
 | |
|   cmd[2] = page_code;
 | |
|   cmd[4] = *buf_size;
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| set_window (BH_Scanner *s, SANE_Byte batchmode)
 | |
| {
 | |
|   static struct {
 | |
|     SANE_Byte cmd[10];
 | |
|     SANE_Byte hdr[8];
 | |
|     struct window_data window;
 | |
|   } set_window_cmd;
 | |
|   SANE_Status status;
 | |
|   SANE_Int width, length, i, format, rotation, deskew ;
 | |
| 
 | |
|   DBG (3, "set_window called\n");
 | |
| 
 | |
|   /* set to thousandths for set_window */
 | |
|   s->bmu = BH_UNIT_INCH;
 | |
|   s->mud = 1000;
 | |
|   status = mode_select_measurement(s);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     return status;
 | |
| 
 | |
|   memset (&set_window_cmd, 0, sizeof (set_window_cmd));
 | |
|   set_window_cmd.cmd[0] = BH_SCSI_SET_WINDOW;
 | |
|   DBG(3, "set_window: sizeof(hdr) %d, sizeof(window): %d\n",
 | |
|       (int)sizeof(set_window_cmd.hdr), (int)sizeof(set_window_cmd.window));
 | |
| 
 | |
|   _lto3b(sizeof(set_window_cmd.hdr) + sizeof(set_window_cmd.window),
 | |
| 	 &set_window_cmd.cmd[6]);
 | |
| 
 | |
|   _lto2b(256, &set_window_cmd.hdr[6]);
 | |
| 
 | |
|   set_window_cmd.window.windowid = 0;
 | |
|   set_window_cmd.window.autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER);
 | |
|   DBG (5, "autoborder set to=%d\n", set_window_cmd.window.autoborder);
 | |
|   _lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.xres);
 | |
|   _lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.yres);
 | |
|   _lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X), set_window_cmd.window.ulx);
 | |
|   _lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y), set_window_cmd.window.uly);
 | |
| 
 | |
|   width = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X) -
 | |
| 	 _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X));
 | |
|   length = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y) -
 | |
| 	 _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y));
 | |
| 
 | |
|   _lto4b(width, set_window_cmd.window.windowwidth);
 | |
|   _lto4b(length, set_window_cmd.window.windowlength);
 | |
| 
 | |
|   /* brightness (1-255) 0 is default, aka 128.  Ignored with ACE scanners */
 | |
|   set_window_cmd.window.brightness = _OPT_VAL_WORD(s, OPT_BRIGHTNESS);
 | |
|   /* threshold (1-255) 0 is default, aka 128.  Ignored with ACE scanners */
 | |
|   set_window_cmd.window.threshold = _OPT_VAL_WORD(s, OPT_THRESHOLD);
 | |
|   /*!!! contrast (not used) */
 | |
|   /*!!! set_window_cmd.window.contrast = _OPT_VAL_WORD(s, OPT_CONTRAST); */
 | |
|   /* imagecomposition 0x00 lineart, 0x01 dithered/halftone, 0x02 grayscale*/
 | |
|   set_window_cmd.window.imagecomposition =
 | |
|     get_scan_mode_id(_OPT_VAL_STRING(s, OPT_SCAN_MODE));
 | |
| 
 | |
|   set_window_cmd.window.bitsperpixel = 0x01;
 | |
|   /*!!! halftone code (not used) */
 | |
|   /*!!! halftone id (not used) */
 | |
| 
 | |
|   set_window_cmd.window.paddingtype = 0x03; /* truncate byte */
 | |
|   if (_OPT_VAL_WORD(s, OPT_NEGATIVE) == SANE_TRUE) {
 | |
|     /* reverse image format (valid when bitsperpixel=1)
 | |
|      * 0x00 normal, 0x01 reversed.  This is bit 7 of paddingtype.
 | |
|      */
 | |
|     set_window_cmd.window.paddingtype |= 0x80;
 | |
|   }
 | |
| 
 | |
|   set_window_cmd.window.bitordering[0] = 0x00;
 | |
| 
 | |
|   /* we must always sent plain gray data in preview mode */
 | |
|   format = (_OPT_VAL_WORD(s, OPT_PREVIEW)) ?
 | |
|     BH_COMP_NONE :
 | |
|     get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
 | |
| 
 | |
|   switch (format)
 | |
|     {
 | |
|     case BH_COMP_G31D:
 | |
|       set_window_cmd.window.compressiontype = 0x01;
 | |
|       set_window_cmd.window.compressionarg = 0x00;
 | |
|       set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
 | |
|       break;
 | |
|     case BH_COMP_G32D:
 | |
|       set_window_cmd.window.compressiontype = 0x02;
 | |
|       set_window_cmd.window.compressionarg = 0x04;
 | |
|       set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
 | |
|       break;
 | |
|     case BH_COMP_G42D:
 | |
|       set_window_cmd.window.compressiontype = 0x03;
 | |
|       set_window_cmd.window.compressionarg = 0x00;
 | |
|       set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */
 | |
|       break;
 | |
|     case BH_COMP_NONE:
 | |
|     default:
 | |
|       set_window_cmd.window.compressiontype = 0x00;
 | |
|       set_window_cmd.window.compressionarg = 0x00;
 | |
|       set_window_cmd.window.bitordering[1] = 0x00; /* n/a */
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   /* rotation and deskew settings, if autoborder is turned on */
 | |
|   if(set_window_cmd.window.autoborder){ /*--- setting byte 46 of the window descriptor block only works with autoborder */
 | |
|     rotation = get_rotation_id(_OPT_VAL_STRING(s, OPT_ROTATION));
 | |
|     if (_OPT_VAL_WORD(s, OPT_DESKEW) == SANE_TRUE) deskew = BH_DESKEW_ENABLE;
 | |
|     else deskew = BH_DESKEW_DISABLE;
 | |
|     set_window_cmd.window.border_rotation = ( rotation | deskew );  /*--- deskew assumes autoborder */
 | |
|   }
 | |
| 
 | |
|   /* remote - 0x00 ACE set in window; 0x01 ACE set by control panel */
 | |
|   set_window_cmd.window.remote = _OPT_VAL_WORD(s, OPT_CONTROL_PANEL);
 | |
|   if (set_window_cmd.window.remote == 0x00) {
 | |
|     /* acefunction (ignored on non-ACE scanners) */
 | |
|     set_window_cmd.window.acefunction = _OPT_VAL_WORD(s, OPT_ACE_FUNCTION);
 | |
|     /* acesensitivity (ignored on non-ACE scanners) */
 | |
|     set_window_cmd.window.acesensitivity = _OPT_VAL_WORD(s, OPT_ACE_SENSITIVITY);
 | |
|   }
 | |
| 
 | |
|   set_window_cmd.window.batchmode = batchmode;
 | |
| 
 | |
|   /* fill in the section descriptor blocks */
 | |
|   for (i = 0; i < s->num_sections; i++)
 | |
|     {
 | |
|       BH_SectionBlock *b;
 | |
| 
 | |
|       b = &set_window_cmd.window.sectionblock[i];
 | |
| 
 | |
|       _lto4b(s->sections[i].left, b->ul_x);
 | |
|       _lto4b(s->sections[i].top, b->ul_y);
 | |
|       _lto4b(s->sections[i].width, b->width);
 | |
|       _lto4b(s->sections[i].length, b->length);
 | |
|       b->compressiontype = s->sections[i].compressiontype;
 | |
|       b->compressionarg = s->sections[i].compressionarg;
 | |
|     }
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &set_window_cmd, sizeof (set_window_cmd), 0, 0);
 | |
|   DBG (5, "sanei_scsi_cmd executed, status=%d\n", status );
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     return status;
 | |
| 
 | |
|   /* set to points for reading */
 | |
|   s->bmu = BH_UNIT_POINT;
 | |
|   s->mud = 1;
 | |
|   status = mode_select_measurement(s);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| get_window (BH_Scanner *s, SANE_Int *w, SANE_Int *h, SANE_Bool backpage)
 | |
| {
 | |
|   SANE_Byte cmd[10];
 | |
|   static struct {
 | |
|     SANE_Byte hdr[8];
 | |
|     struct window_data window;
 | |
|   } get_window_data;
 | |
|   SANE_Status status;
 | |
|   SANE_Int x, y, i = 0, get_window_delay = 1;
 | |
|   SANE_Bool autoborder;
 | |
|   size_t len;
 | |
| 
 | |
|   DBG (3, "get_window called\n");
 | |
| 
 | |
|   autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER) == 1;
 | |
| 
 | |
|   while (1)
 | |
|     {
 | |
|       i++;
 | |
|       memset (&cmd, 0, sizeof (cmd));
 | |
|       memset (&get_window_data, 0, sizeof (get_window_data));
 | |
| 
 | |
|       cmd[0] = BH_SCSI_GET_WINDOW;
 | |
|       _lto3b(sizeof(get_window_data), &cmd[6]);
 | |
| 
 | |
|       _lto2b(256, &get_window_data.hdr[6]);
 | |
| 
 | |
|       get_window_data.window.windowid = (backpage == SANE_TRUE) ? 1 : 0;
 | |
| 
 | |
|       len = sizeof(get_window_data);
 | |
|       status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd),
 | |
| 			       &get_window_data, &len);
 | |
|       if (status == SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  x =_4btol(get_window_data.window.ulx);
 | |
| 	  y =_4btol(get_window_data.window.uly);
 | |
| 	  *w =_4btol(get_window_data.window.windowwidth);
 | |
| 	  *h =_4btol(get_window_data.window.windowlength);
 | |
| 
 | |
| 	  if (autoborder)
 | |
| 	    {
 | |
| 	      /* we try repeatedly until we get the autoborder bit set */
 | |
| 	      if (get_window_data.window.autoborder != 1 &&
 | |
| 		  i < BH_AUTOBORDER_TRIES)
 | |
| 		{
 | |
| 	          DBG (5, "waiting %d second[s], try: %d\n",get_window_delay,i);
 | |
| 		  sleep(get_window_delay);  /*--- page 4-5 of B&H Copiscan 8000 ESC OEM Tech Manual */
 | |
|                                             /*--- requires at least 50ms wait between each GET WINDOW command */
 | |
|                                             /*--- experience shows that this can take 3 to 4 seconds */
 | |
| 		  continue;
 | |
| 		}
 | |
| 	      if (get_window_data.window.autoborder != 1)
 | |
| 		{
 | |
| 		  DBG(1, "Automatic Border Detection not done within %d tries\n",
 | |
| 		      BH_AUTOBORDER_TRIES);
 | |
| 		  status = SANE_STATUS_IO_ERROR;
 | |
| 		}
 | |
|              DBG (0, "page dimension: wide:%d high:%d \n",*w,*h);
 | |
| 	    }
 | |
| 	  DBG (3, "*** Window size: %dx%d+%d+%d\n", *w, *h, x, y);
 | |
| 	  DBG (5, "*** get_window found autoborder=%02xh\n", get_window_data.window.autoborder);
 | |
| 	  DBG (5, "*** get_window found border_rotation=%02xh\n", get_window_data.window.border_rotation);
 | |
| 	}
 | |
| 
 | |
|       /* we are 'outta here' */
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| get_parameters (SANE_Handle handle, SANE_Parameters *params)
 | |
| {
 | |
|   BH_Scanner *s = handle;
 | |
|   SANE_Int width, length, res, comp;
 | |
|   double br_x, tl_x, br_y, tl_y;
 | |
|   SANE_Frame format;
 | |
| 
 | |
|   DBG(3, "get_parameters called\n");
 | |
| 
 | |
|   memset (&s->params, 0, sizeof (s->params));
 | |
| 
 | |
|   res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
 | |
| 
 | |
|   /* make best-effort guess at what parameters will look like once
 | |
|      the scan starts.  */
 | |
| 
 | |
|   br_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X);
 | |
|   br_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y);
 | |
|   tl_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X);
 | |
|   tl_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y);
 | |
| 
 | |
|   width = (br_x - tl_x + 1) * res / 1000.0;
 | |
|   length = (br_y - tl_y + 1) * res / 1000.0;
 | |
| 
 | |
|   /* figure out the default image format for front/back pages */
 | |
|   comp = get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
 | |
|   switch (comp)
 | |
|     {
 | |
|     case BH_COMP_G31D:
 | |
|       format = SANE_FRAME_G31D;
 | |
|       break;
 | |
|     case BH_COMP_G32D:
 | |
|       format = SANE_FRAME_G32D;
 | |
|       break;
 | |
|     case BH_COMP_G42D:
 | |
|       format = SANE_FRAME_G42D;
 | |
|       break;
 | |
|     case BH_COMP_NONE:
 | |
|     default:
 | |
|       format = SANE_FRAME_GRAY;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   if (s->scanning)
 | |
|     {
 | |
|       SANE_Int w, l, status;
 | |
|       SANE_Byte itemtype;
 | |
| 
 | |
|       itemtype = s->readlist[s->readptr];
 | |
|       /* update parameters based on the current item */
 | |
| 
 | |
|       status = SANE_STATUS_GOOD;
 | |
|       if (itemtype == BH_SCSI_READ_TYPE_FRONT)
 | |
| 	{
 | |
| 	  DBG (3, "get_parameters: sending GET WINDOW (front)\n");
 | |
| 	  status = get_window (s, &w, &l, SANE_FALSE);
 | |
| 	  if (status == SANE_STATUS_GOOD)
 | |
| 	    {
 | |
| 	      width = w;
 | |
| 	      length = l;
 | |
| 	    }
 | |
| 	}
 | |
|       else if (itemtype == BH_SCSI_READ_TYPE_BACK)
 | |
| 	{
 | |
| 	  DBG (3, "get_parameters: sending GET WINDOW (back)\n");
 | |
| 	  status = get_window (s, &w, &l, SANE_TRUE);
 | |
| 	  if (status == SANE_STATUS_GOOD)
 | |
| 	    {
 | |
| 	      width = w;
 | |
| 	      length = l;
 | |
| 	    }
 | |
| 	}
 | |
|       else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON ||
 | |
| 	       itemtype == BH_SCSI_READ_TYPE_BACK_ICON)
 | |
| 	{
 | |
| 	  /* the icon is never compressed */
 | |
| 	  format = SANE_FRAME_GRAY;
 | |
| 	  width = s->iconwidth;
 | |
| 	  length = s->iconlength;
 | |
| 	}
 | |
|       else if (itemtype > BH_SCSI_READ_TYPE_FRONT &&
 | |
| 	       itemtype <= (BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS))
 | |
| 	{
 | |
| 	  /* a front section */
 | |
| 	  SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_FRONT;
 | |
| 
 | |
| 	  format = s->sections[sectnum - 1].format;
 | |
| 	  /* convert from thousandths to pixels */
 | |
| 	  width = s->sections[sectnum - 1].width * res / 1000.0;
 | |
| 	  length = s->sections[sectnum - 1].length * res / 1000.0;
 | |
| 	}
 | |
|       else if (itemtype > BH_SCSI_READ_TYPE_BACK &&
 | |
| 	       itemtype <= (BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS))
 | |
| 	{
 | |
| 	  /* a back section */
 | |
| 	  SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_BACK;
 | |
| 
 | |
| 	  format = s->sections[sectnum - 1].format;
 | |
| 	  /* convert from thousandths to pixels */
 | |
| 	  width = s->sections[sectnum - 1].width * res / 1000.0;
 | |
| 	  length = s->sections[sectnum - 1].length * res / 1000.0;
 | |
| 	}
 | |
|       else if ( (itemtype >= BH_SCSI_READ_TYPE_BACK_BARCODE &&
 | |
| 		 itemtype <= (BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)) ||
 | |
| 		(itemtype >= BH_SCSI_READ_TYPE_FRONT_BARCODE &&
 | |
| 		 itemtype <= (BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)) )
 | |
| 	{
 | |
| 	  /* decoded barcode data */
 | |
| 	  format = SANE_FRAME_TEXT;
 | |
| 	  width = 8;
 | |
| 	  length = -1;
 | |
| 	}
 | |
|       else if (itemtype == BH_SCSI_READ_TYPE_SENDBARFILE)
 | |
| 	{
 | |
| 	  /* decoded barcode data file */
 | |
| 	  format = SANE_FRAME_TEXT;
 | |
| 	  width = 8;
 | |
| 	  length = -1;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  format = SANE_FRAME_GRAY;
 | |
| 	  width = 8;
 | |
| 	  length = -1;
 | |
| 	  DBG(1, "get_parameters: unrecognized read itemtype: %d\n",
 | |
| 	      itemtype);
 | |
| 	}
 | |
| 
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "get_parameters: failed\n");
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (res <= 0 || width <= 0)
 | |
|     {
 | |
|       DBG(1, "get_parameters:illegal parameters res=%d, width=%d, length=%d\n",
 | |
| 	  res, width, length);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* we disable our compression/barcode formats in preview as well
 | |
|    * as with the disable_optional_frames configuration option.  NOTE:
 | |
|    * we may still be delivering 'wierd' data and lying about it being _GRAY!
 | |
|    */
 | |
|   if (format != SANE_FRAME_GRAY &&
 | |
|       (_OPT_VAL_WORD(s, OPT_PREVIEW) || disable_optional_frames))
 | |
|     {
 | |
|       DBG(1, "get_parameters: warning: delivering %s data as gray",
 | |
| 	  sane_strframe(format));
 | |
|       format = SANE_FRAME_GRAY;
 | |
|     }
 | |
| 
 | |
|   s->params.format = format;
 | |
|   s->params.depth = 1;
 | |
|   s->params.last_frame = SANE_TRUE;
 | |
|   s->params.pixels_per_line = width;
 | |
|   s->params.lines = length;
 | |
|   s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
 | |
|   /* The Bell and Howell truncates to the byte */
 | |
|   s->params.pixels_per_line = s->params.bytes_per_line * 8;
 | |
| 
 | |
|   if (params)
 | |
|     *params = s->params;
 | |
| 
 | |
|   DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, "
 | |
|        "lines=%d, dpi=%d\n",
 | |
|        (int) s->params.format,
 | |
|        s->params.pixels_per_line,
 | |
|        s->params.bytes_per_line,
 | |
|        s->params.lines,
 | |
|        res);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| section_parse(const char *val, BH_Section *sect, SANE_Int res, SANE_Int comp)
 | |
| {
 | |
|   SANE_Status status = SANE_STATUS_INVAL;
 | |
|   char buf[255+1], *x, *y, *w, *l, *f, *ep;
 | |
|   const char *seps = "x+:";
 | |
|   double mm, fpixels;
 | |
|   u_long pixels;
 | |
| 
 | |
|   DBG(3, "section_parse called\n");
 | |
| 
 | |
|   /* a section option looks something like this:
 | |
|    * <width>x<length>+<tl-x>+<tl-y>:<functioncodes>
 | |
|    * Example:
 | |
|    * 76.2x25.4+50.8+0:frontbar:back:front
 | |
|    * the width, length, tl-x, and tl-y are in mm.
 | |
|    * the function codes are one or more of:
 | |
|    * front, back, frontbar, backbar, frontpatch, backpatch
 | |
|    */
 | |
|   if (strlen(val) > sizeof(buf) - 1)
 | |
|     {
 | |
|       DBG(1, "section_parse: option string too long\n");
 | |
|       status = SANE_STATUS_INVAL;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       do {
 | |
| 	strcpy(buf, val);
 | |
| 
 | |
| 	x = y = w = l = f = NULL;
 | |
| 	w = strtok(buf, seps);
 | |
| 	if (w) l = strtok(NULL, seps);
 | |
| 	if (l) x = strtok(NULL, seps);
 | |
| 	if (x) y = strtok(NULL, seps);
 | |
| 	if (y) f = strtok(NULL, seps);
 | |
| 	if (!x || !y || !w || !l) break;
 | |
| 
 | |
| 	mm = strtod(x, &ep);
 | |
| 	if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
 | |
| 	sect->left = mm * 1000.0 / MM_PER_INCH;
 | |
| 
 | |
| 	mm = strtod(y, &ep);
 | |
| 	if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
 | |
| 	sect->top = mm * 1000.0 / MM_PER_INCH;
 | |
| 
 | |
| 	mm = strtod(w, &ep);
 | |
| 	if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
 | |
| 	sect->width = mm * 1000.0 / MM_PER_INCH;
 | |
| 	/* the window width must be truncated to 16 bit points */
 | |
| 	fpixels = sect->width * res / 1000.0;
 | |
| 	pixels = fpixels / 16;
 | |
| 	sect->width = pixels * 16 * 1000 / res;
 | |
| 
 | |
| 	mm = strtod(l, &ep);
 | |
| 	if (*ep != '\0' || errno == ERANGE || mm < 0.0) break;
 | |
| 	sect->length = mm * 1000.0 / MM_PER_INCH;
 | |
| 
 | |
| 	status = SANE_STATUS_GOOD;
 | |
| 	while (f)
 | |
| 	  {
 | |
| 	    /* parse the function modifiers and set flags */
 | |
| 	    if (strcmp(f, "front") == 0)
 | |
| 	      sect->flags |= BH_SECTION_FRONT_IMAGE;
 | |
| 	    else if (strcmp(f, "frontbar") == 0)
 | |
| 	      sect->flags |= BH_SECTION_FRONT_BAR;
 | |
| 	    else if (strcmp(f, "frontpatch") == 0)
 | |
| 	      sect->flags |= BH_SECTION_FRONT_PATCH;
 | |
| 	    else if (strcmp(f, "back") == 0)
 | |
| 		sect->flags |= BH_SECTION_BACK_IMAGE;
 | |
| 	    else if (strcmp(f, "backbar") == 0)
 | |
| 		sect->flags |= BH_SECTION_BACK_BAR;
 | |
| 	    else if (strcmp(f, "backpatch") == 0)
 | |
| 		sect->flags |= BH_SECTION_BACK_PATCH;
 | |
| 	    else if (strcmp(f, "g42d") == 0)
 | |
| 		comp = BH_COMP_G42D;
 | |
| 	    else if (strcmp(f, "g32d") == 0)
 | |
| 		comp = BH_COMP_G32D;
 | |
| 	    else if (strcmp(f, "g31d") == 0)
 | |
| 		comp = BH_COMP_G31D;
 | |
| 	    else if (strcmp(f, "none") == 0)
 | |
| 		comp = BH_COMP_NONE;
 | |
| 	    else
 | |
| 	      DBG(1, "section_parse: ignoring unrecognized function "
 | |
| 		  "code '%s'\n", f);
 | |
| 
 | |
| 	    f = strtok(NULL, seps);
 | |
| 	  }
 | |
| 
 | |
| 	switch (comp)
 | |
| 	  {
 | |
| 	  case BH_COMP_G31D:
 | |
| 	    sect->compressiontype = 0x01;
 | |
| 	    sect->compressionarg = 0x00;
 | |
| 	    sect->format = SANE_FRAME_G31D;
 | |
| 	    break;
 | |
| 	  case BH_COMP_G32D:
 | |
| 	    sect->compressiontype = 0x02;
 | |
| 	    sect->compressionarg = 0x04;
 | |
| 	    sect->format = SANE_FRAME_G32D;
 | |
| 	    break;
 | |
| 	  case BH_COMP_G42D:
 | |
| 	    sect->compressiontype = 0x03;
 | |
| 	    sect->compressionarg = 0x00;
 | |
| 	    sect->format = SANE_FRAME_G42D;
 | |
| 	    break;
 | |
| 	  case BH_COMP_NONE:
 | |
| 	  default:
 | |
| 	    sect->compressiontype = 0x00;
 | |
| 	    sect->compressionarg = 0x00;
 | |
| 	    sect->format = SANE_FRAME_GRAY;
 | |
| 	    break;
 | |
| 	  }
 | |
| 
 | |
| 	DBG(3, "section_parse: converted '%s' (mm) to "
 | |
| 	    "%ldx%ld+%ld+%ld (thousandths) "
 | |
| 	    "flags=%02x compression=[%d,%d] frame=%s\n",
 | |
| 	    val,
 | |
| 	    sect->width, sect->length, sect->left, sect->top,
 | |
| 	    sect->flags,
 | |
| 	    sect->compressiontype, sect->compressionarg,
 | |
| 	    sane_strframe(sect->format));
 | |
| 
 | |
|       } while (0); /* perform 'loop' once */
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| setup_sections (BH_Scanner *s, const char *val)
 | |
| {
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   SANE_Int sectnum = 0;
 | |
|   char buf[255+1], *section;
 | |
| 
 | |
|   DBG(3, "setup_sections called\n");
 | |
| 
 | |
|   memset(s->sections, '\0', sizeof(s->sections));
 | |
|   if (strlen(val) > sizeof(buf) - 1)
 | |
|     {
 | |
|       DBG(1, "setup_sections: option string too long\n");
 | |
|       status = SANE_STATUS_INVAL;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       strcpy(buf, val);
 | |
| 
 | |
|       section = strtok(buf, ",");
 | |
|       while (section != NULL && sectnum < NUM_SECTIONS)
 | |
| 	{
 | |
| 	  if (!allblank(section))
 | |
| 	    {
 | |
| 	      SANE_Int res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
 | |
| 	      SANE_Int format =
 | |
| 		get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION));
 | |
| 
 | |
| 	      status = section_parse(section, &s->sections[sectnum],
 | |
| 				     res, format);
 | |
| 	      if (status != SANE_STATUS_GOOD)
 | |
| 		{
 | |
| 		  DBG(1,
 | |
| 		      "setup_sections: error parsing section `%s'\n",
 | |
| 		      section);
 | |
| 		  break;
 | |
| 		}
 | |
| 
 | |
| 	      sectnum++;
 | |
| 	    }
 | |
| 	  section += strlen(section) + 1;
 | |
| 	  if (section > buf + strlen(val)) break;
 | |
| 
 | |
| 	  section = strtok(section, ",");
 | |
| 	}
 | |
|     }
 | |
|   s->num_sections = sectnum;
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| start_setup (BH_Scanner *s)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   SANE_Bool duplex;
 | |
|   SANE_Int i, imagecnt;
 | |
|   SANE_Byte batchmode;
 | |
| 
 | |
|   DBG(3, "start_setup called\n");
 | |
| 
 | |
|   duplex = _OPT_VAL_WORD(s, OPT_DUPLEX);
 | |
| 
 | |
|   /* get the _SECTION option, parse it and fill in the sections */
 | |
|   status = setup_sections(s, _OPT_VAL_STRING(s, OPT_SECTION));
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG(1, "start_setup: setup_sections failed: %s\n",
 | |
| 	  sane_strstatus(status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   /* see whether we'll be decoding barcodes and
 | |
|    * set the barcodes flag appropriately
 | |
|    */
 | |
|   if (s->search_bars[0] == 0)
 | |
|     {
 | |
|       s->barcodes = SANE_FALSE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       s->barcodes = SANE_TRUE;
 | |
|     }
 | |
| 
 | |
|   /* see whether we'll be handling icons (thumbnails)
 | |
|    * set the icons flag appropriately
 | |
|    */
 | |
|   if (_OPT_VAL_WORD(s, OPT_ICON_WIDTH) >= 8 &&
 | |
|       _OPT_VAL_WORD(s, OPT_ICON_LENGTH) >= 8)
 | |
|     {
 | |
|       s->icons = SANE_TRUE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       s->icons = SANE_FALSE;
 | |
|     }
 | |
| 
 | |
| 
 | |
|   /* calculate a new readlist for this 'batch' */
 | |
|   s->readptr = s->readcnt = 0;
 | |
| 
 | |
|   /* always read the front image */
 | |
|   s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT;
 | |
| 
 | |
|   /* read back page only if duplex is true */
 | |
|   if (duplex == SANE_TRUE)
 | |
|     {
 | |
|       s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK;
 | |
|     }
 | |
| 
 | |
|   /* add image section reads to the readlist */
 | |
|   for (i = 0; i < s->num_sections; i++)
 | |
|     {
 | |
|       SANE_Word flags = s->sections[i].flags;
 | |
| 
 | |
|       if (flags & BH_SECTION_FRONT_IMAGE)
 | |
| 	s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT + i + 1;
 | |
|       if (flags & BH_SECTION_BACK_IMAGE)
 | |
| 	s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK + i + 1;
 | |
|     }
 | |
| 
 | |
| 
 | |
|   /* icons (thumbnails) */
 | |
|   if (s->icons)
 | |
|     {
 | |
|       s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_ICON;
 | |
|       /* read back icon only if duplex is true */
 | |
|       if (duplex == SANE_TRUE)
 | |
| 	{
 | |
| 	  s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_ICON;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* NOTE: It is important that all of the image data comes before
 | |
|    * the barcode/patchcode data.
 | |
|    */
 | |
|   /* barcodes */
 | |
|   imagecnt = s->readcnt;
 | |
|   if (s->barcodes)
 | |
|     {
 | |
|       if (s->num_sections == 0)
 | |
| 	{
 | |
| 	  /* we only decode the entire page(s) if there are no
 | |
| 	   * sections defined
 | |
| 	   */
 | |
| 	  s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_BARCODE;
 | |
| 	  /* read back barcode only if duplex is true */
 | |
| 	  if (duplex == SANE_TRUE)
 | |
| 	    {
 | |
| 	      s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_BARCODE;
 | |
| 	    }
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  /* add barcode section reads to the readlist */
 | |
| 	  for (i = 0; i < s->num_sections; i++)
 | |
| 	    {
 | |
| 	      SANE_Word flags = s->sections[i].flags;
 | |
| 
 | |
| 	      if (flags & BH_SECTION_FRONT_BAR)
 | |
| 		s->readlist[s->readcnt++] =
 | |
| 		  BH_SCSI_READ_TYPE_FRONT_BARCODE + i + 1;
 | |
| 	      if (flags & BH_SECTION_BACK_BAR)
 | |
| 		s->readlist[s->readcnt++] =
 | |
| 		  BH_SCSI_READ_TYPE_BACK_BARCODE + i + 1;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* patchcodes */
 | |
|   if (s->patchcodes)
 | |
|     {
 | |
|       if (s->num_sections == 0)
 | |
| 	{
 | |
| 	  /* we only decode the entire page(s) if there are no
 | |
| 	   * sections defined
 | |
| 	   */
 | |
| 	  s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_PATCHCODE;
 | |
| 	  /* read back patchcode only if duplex is true */
 | |
| 	  if (duplex == SANE_TRUE)
 | |
| 	    {
 | |
| 	      s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_PATCHCODE;
 | |
| 	    }
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  /* add patchcode section reads to the readlist */
 | |
| 	  for (i = 0; i < s->num_sections; i++)
 | |
| 	    {
 | |
| 	      SANE_Word flags = s->sections[i].flags;
 | |
| 
 | |
| 	      if (flags & BH_SECTION_FRONT_PATCH)
 | |
| 		s->readlist[s->readcnt++] =
 | |
| 		  BH_SCSI_READ_TYPE_FRONT_PATCHCODE + i + 1;
 | |
| 	      if (flags & BH_SECTION_BACK_PATCH)
 | |
| 		s->readlist[s->readcnt++] =
 | |
| 		  BH_SCSI_READ_TYPE_BACK_PATCHCODE + i + 1;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* add the special item to the read list which transfers the barcode
 | |
|    * file that's built as a result of processing barcode and patchcode
 | |
|    * readitems.  NOTE: this one must be last!
 | |
|    */
 | |
|   if (s->readcnt > imagecnt)
 | |
|     {
 | |
|       s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_SENDBARFILE;
 | |
|     }
 | |
| 
 | |
|   if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE)
 | |
|     {
 | |
|       /* if batchmode is enabled, then call set_window to
 | |
|        * abort the batch (even though there might not (and probably
 | |
|        * isn't) a batch in progress).  This avoids a batch start error
 | |
|        * in the case where a previous batch was not aborted.
 | |
|        */
 | |
|       DBG(5, "start_setup: calling set_window to abort batch\n");
 | |
|       set_window(s, BH_BATCH_ABORT);
 | |
| 
 | |
|       batchmode = BH_BATCH_ENABLE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       batchmode = BH_BATCH_DISABLE;
 | |
|     }
 | |
| 
 | |
|   DBG(5, "start_setup: duplex=%s, barcodes=%s, patchcodes=%s, "
 | |
|       "icons=%s, batch=%s\n",
 | |
|       (duplex == SANE_TRUE) ? "yes" : "no",
 | |
|       (s->barcodes == SANE_TRUE) ? "yes" : "no",
 | |
|       (s->patchcodes == SANE_TRUE) ? "yes" : "no",
 | |
|       (s->icons == SANE_TRUE) ? "yes" : "no",
 | |
|       (batchmode == BH_BATCH_ENABLE) ? "yes" : "no");
 | |
|   DBG(5, "start_setup: sections=%d\n", s->num_sections);
 | |
|   for (i = 0; i < s->num_sections; i++)
 | |
|     {
 | |
|       DBG(5, "start_setup:  "
 | |
| 	  "[%d] %lux%lu+%lu+%lu flags=%02x compression=[%d,%d]\n",
 | |
| 	  i+1,
 | |
| 	  s->sections[i].width, s->sections[i].length,
 | |
| 	  s->sections[i].left, s->sections[i].top,
 | |
| 	  s->sections[i].flags,
 | |
| 	  s->sections[i].compressiontype, s->sections[i].compressionarg);
 | |
|     }
 | |
|   DBG(5, "start_setup: read list length=%d\n", s->readcnt);
 | |
|   for (i = 0; i < s->readcnt; i++)
 | |
|     {
 | |
|       DBG(5, "start_setup:  [%d] %s\n", i+1, print_read_type(s->readlist[i]));
 | |
|     }
 | |
| 
 | |
|   DBG(5, "start_setup: sending SET WINDOW\n");
 | |
|   status = set_window(s, batchmode);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG(1, "start_setup: SET WINDOW failed: %s\n",
 | |
| 	   sane_strstatus(status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   DBG(5, "start_setup: sending mode_select_timeout\n");
 | |
|   status = mode_select_timeout(s);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG(1, "start_setup: mode_select_timeout failed: %s\n",
 | |
| 	   sane_strstatus(status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (s->icons == SANE_TRUE)
 | |
|     {
 | |
|       DBG(5, "start_setup: sending mode_select_icon\n");
 | |
|       status = mode_select_icon(s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "start_setup: mode_select_icon failed: %s\n",
 | |
| 	       sane_strstatus(status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (s->barcodes == SANE_TRUE)
 | |
|     {
 | |
|       DBG(5, "start_setup: sending mode_select_barcode_priority\n");
 | |
|       status = mode_select_barcode_priority(s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "start_setup: mode_select_barcode_priority failed: %s\n",
 | |
| 	       sane_strstatus(status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG(5, "start_setup: sending mode_select_barcode_param1\n");
 | |
|       status = mode_select_barcode_param1(s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "start_setup: mode_select_barcode_param1 failed: %s\n",
 | |
| 	       sane_strstatus(status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG(5, "start_setup: sending mode_select_barcode_param2\n");
 | |
|       status = mode_select_barcode_param2(s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "start_setup: mode_select_barcode_param2 failed: %s\n",
 | |
| 	       sane_strstatus(status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG(5, "start_setup: sending mode_select_barcode_param3\n");
 | |
|       status = mode_select_barcode_param3(s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "start_setup: mode_select_barcode_param3 failed: %s\n",
 | |
| 	       sane_strstatus(status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| start_scan (BH_Scanner *s)
 | |
| {
 | |
|   static SANE_Byte cmd[8];
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   SANE_Bool check_adf, duplex;
 | |
|   DBG (3, "start_scan called\n");
 | |
| 
 | |
|   /* SANE front ends will call this function between 'FRAMES'.
 | |
|    * A single scan on the B&H may result in up to 56 different
 | |
|    * things to read (20 are SANE image frames, 36 are non-SANE
 | |
|    * data - decoded bar/patch codes).
 | |
|    */
 | |
| 
 | |
|   if (s->readcnt > 1 && s->scanning == SANE_TRUE)
 | |
|     {
 | |
|       DBG(3, "start_scan: any more items in the readlist?\n");
 | |
|       /* we've been reading data from this scan, so we just
 | |
|        * move on to the next item in the readlist without
 | |
|        * starting a new scan.
 | |
|        */
 | |
|       s->readptr++;
 | |
|       if (s->readptr < s->readcnt)
 | |
| 	{
 | |
| 	  SANE_Byte itemtype;
 | |
| 
 | |
| 	  for (; s->readptr < s->readcnt; s->readptr++)
 | |
| 	    {
 | |
| 
 | |
| 	      itemtype = s->readlist[s->readptr];
 | |
| 
 | |
| 	      DBG(3, "start_scan: advance readlist(%d, %d)\n",
 | |
| 		  s->readptr,
 | |
| 		  (int) itemtype);
 | |
| 
 | |
| 	      /* 'dance' by the non-SANE data streams
 | |
| 	       * like bar/patch code data
 | |
| 	       */
 | |
| 	      if (!BH_HAS_IMAGE_DATA(itemtype))
 | |
| 		{
 | |
| 		  int fd;
 | |
| 		  FILE *fp;
 | |
| 
 | |
| 		  strncpy(s->barfname, "/tmp/bhXXXXXX", sizeof(s->barfname));
 | |
| 		  s->barfname[sizeof(s->barfname)-1] = '\0';
 | |
| 		  fd = mkstemp(s->barfname);
 | |
| 
 | |
| 		  if (fd !=-1 && (fp = fdopen(fd, "w")) != NULL)
 | |
| 		    {
 | |
| 		      fprintf(fp, "<xml-stream>\n");
 | |
| 
 | |
| 		      for (;
 | |
| 			   s->readptr < s->readcnt &&
 | |
| 			     status == SANE_STATUS_GOOD;
 | |
| 			   s->readptr++)
 | |
| 			{
 | |
| 			  if (s->readlist[s->readptr] ==
 | |
| 			      BH_SCSI_READ_TYPE_SENDBARFILE) {
 | |
| 			    break;
 | |
| 			  }
 | |
| 			  status = read_barcode_data(s, fp);
 | |
| 			  if (status != SANE_STATUS_GOOD) break;
 | |
| 			}
 | |
| 
 | |
| 		      fprintf(fp, "</xml-stream>\n");
 | |
| 
 | |
| 		      /* close file; re-open for read(setting s->barfd) */
 | |
| 		      fclose(fp);
 | |
| 		      if ((s->barf = fopen(s->barfname, "r")) == NULL)
 | |
| 			{
 | |
| 			  DBG(1, "sane_start: error opening barfile `%s'\n",
 | |
| 			      s->barfname);
 | |
| 			  status = SANE_STATUS_IO_ERROR;
 | |
| 			}
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      DBG(1, "sane_start: error opening barfile `%s'\n",
 | |
| 			  s->barfname);
 | |
| 		      status = SANE_STATUS_IO_ERROR;
 | |
| 		    }
 | |
| 		}
 | |
| 	      else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON ||
 | |
| 		       itemtype == BH_SCSI_READ_TYPE_BACK_ICON)
 | |
| 		{
 | |
| 		  /* read the icon header setting the iconwidth and iconlength
 | |
| 		   * to the actual values so get_parameters will have them.
 | |
| 		   * Subsequent calls to sane_read will get pure image data
 | |
| 		   * since the icon header has been consumed.
 | |
| 		   */
 | |
| 
 | |
| 		  status = read_icon_data(s);
 | |
| 		}
 | |
| 
 | |
| 	      if (status == SANE_STATUS_GOOD)
 | |
| 		{
 | |
| 		  /* update our parameters to reflect the new item */
 | |
| 		  status = get_parameters (s, 0);
 | |
| 		}
 | |
| 
 | |
| 	      if (status != SANE_STATUS_GOOD) s->scanning = SANE_FALSE;
 | |
| 
 | |
| 	      return status;
 | |
| 	    }
 | |
| 	  /* if we reach here, we're finished with the readlist and
 | |
| 	   * will drop through to start a new scan
 | |
| 	   */
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   s->readptr = 0;
 | |
| 
 | |
|   check_adf = _OPT_VAL_WORD(s, OPT_CHECK_ADF);
 | |
|   duplex = _OPT_VAL_WORD(s, OPT_DUPLEX);
 | |
| 
 | |
|   memset (&cmd, 0, sizeof (cmd));
 | |
|   cmd[0] = BH_SCSI_START_SCAN;
 | |
|   cmd[4] = (duplex == SANE_TRUE) ? 2 : 1;
 | |
| 
 | |
|   cmd[6] = 0;
 | |
|   cmd[7] = 1;
 | |
| 
 | |
|   if (check_adf)
 | |
|     {
 | |
|       status = object_position(s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(3, "object_position: returned %d\n", status);
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0);
 | |
|   if (status == SANE_STATUS_GOOD)
 | |
|     {
 | |
|       s->scanning = SANE_TRUE;
 | |
| 
 | |
|       /* update our parameters,
 | |
|        * now that we're scanning we'll do a GET_WINDOW
 | |
|        */
 | |
|       status = get_parameters (s, 0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  s->scanning = SANE_FALSE;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /* a sensible sense handler, courtesy of Franck;
 | |
|    arg is a pointer to the associated BH_Scanner structure */
 | |
| static SANE_Status
 | |
| sense_handler (int scsi_fd, u_char *result, void *arg)
 | |
| {
 | |
|   BH_Scanner *s = (BH_Scanner *) arg;
 | |
|   u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData;
 | |
|   u_long InvalidBytes;
 | |
|   char *sense_str = "", *as_str = "";
 | |
|   SANE_Int i;
 | |
|   SANE_Status status = SANE_STATUS_INVAL;
 | |
|   SANE_Char print_sense[(16 * 3) + 1];
 | |
| 
 | |
|   scsi_fd = scsi_fd; /* get rid of compiler warning */
 | |
|   ErrorCode = result[0] & 0x7F;
 | |
|   ValidData = (result[0] & 0x80) != 0;
 | |
|   sense = result[2] & 0x0f; /* Key */
 | |
|   asc = result[12]; /* Code */
 | |
|   ascq = result[13]; /* Qual */
 | |
|   EOM = (result[2] & 0x40) != 0; /* End Of Media */
 | |
|   ILI = (result[2] & 0x20) != 0; /* Invalid Length Indicator */
 | |
|   InvalidBytes = ValidData ? _4btol(&result[3]) : 0;
 | |
| 
 | |
|   DBG(3, "sense_handler: result=%x, sense=%x, asc=%x, ascq=%x\n",
 | |
|       result[0], sense, asc, ascq);
 | |
|   DBG(3, "sense_handler: ErrorCode %02x ValidData: %d "
 | |
|       "EOM: %d ILI: %d InvalidBytes: %lu\n",
 | |
|       ErrorCode, ValidData, EOM, ILI, InvalidBytes);
 | |
| 
 | |
|   memset(print_sense, '\0', sizeof(print_sense));
 | |
|   for (i = 0; i < 16; i++)
 | |
|     {
 | |
|       sprintf(print_sense + strlen(print_sense), "%02x ", result[i]);
 | |
|     }
 | |
|   DBG(5, "sense_handler: sense=%s\n", print_sense);
 | |
| 
 | |
|   if (ErrorCode != 0x70 && ErrorCode != 0x71)
 | |
|     {
 | |
|       DBG (3, "sense_handler: error code is invalid.\n");
 | |
|       return SANE_STATUS_IO_ERROR;	/* error code is invalid */
 | |
|     }
 | |
| 
 | |
|   /* handle each sense key;
 | |
|    * RSC supports 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B
 | |
|    */
 | |
|   switch (sense)
 | |
|     {
 | |
|     case 0x00:
 | |
|       /* no sense */
 | |
|       sense_str = "No sense.";
 | |
|       status = SANE_STATUS_GOOD;
 | |
|       if (ILI && asc == 0x00 && ascq == 0x05)
 | |
| 	{
 | |
| 	  /* from read_data function */
 | |
| 	  as_str = "ILI bit is set.";
 | |
| 	  if (s != NULL)
 | |
| 	    {
 | |
| 	      s->InvalidBytes = InvalidBytes;
 | |
| 	    }
 | |
| 	  status = SANE_STATUS_GOOD;
 | |
| 	}
 | |
|       else if (EOM && asc == 0x00 && ascq == 0x02)
 | |
| 	{
 | |
| 	  /* from adfStatus or startScan function */
 | |
| 	  as_str = "Out of paper in the hopper.";
 | |
| 	  status = SANE_STATUS_NO_DOCS;
 | |
| 	}
 | |
|       else if (EOM)
 | |
| 	{
 | |
| 	  /* from adfStatus or startScan function */
 | |
| 	  as_str = "Out of paper in the hopper.";
 | |
| 	  status = SANE_STATUS_NO_DOCS;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x01:
 | |
|       /* recovered error */
 | |
|       sense_str = "Recovered error.";
 | |
|       status = SANE_STATUS_GOOD;
 | |
|       break;
 | |
|     case 0x02:
 | |
|       /* not ready */
 | |
|       sense_str = "Not ready.";
 | |
|       status = SANE_STATUS_DEVICE_BUSY;
 | |
|       if (asc == 0x40 && ascq == 0x01)
 | |
| 	{
 | |
| 	  as_str = "P.O.D. error: Scanner not found.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x40 && ascq == 0x02)
 | |
| 	{
 | |
| 	  as_str = "P.O.D. error: Scanner not ready(paper in transport).";
 | |
| 	  status = SANE_STATUS_DEVICE_BUSY;
 | |
| 	}
 | |
|       else if (asc == 0x40 && ascq == 0x03)
 | |
| 	{
 | |
| 	  as_str = "P.O.D. error: Unknown scanner.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x03:
 | |
|       /* medium error */
 | |
|       sense_str = "Medium error.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       if (asc == 0x00 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: paper jam detected.";
 | |
| 	  status = SANE_STATUS_JAMMED;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x04:
 | |
|       /* hardware error */
 | |
|       sense_str = "Hardware error.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       if (asc == 0x60 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: illumination lamps failure.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x03)
 | |
| 	{
 | |
| 	  as_str = "Communication error between RSC and scanner.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x06)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: page detected but lamps are off.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x07)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: camera white level problem.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x08)
 | |
| 	{
 | |
| 	  /* could be caught from start_scan or read_data */
 | |
| 	  /* stop button pressed */
 | |
| 	  as_str = "Scanner error: operator pressed the Stop key.";
 | |
| 	  status = SANE_STATUS_NO_DOCS;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x12)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: transport motor failure.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x15)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: device / page sensor(s) bouncing.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x16)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: feeder is not attached.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x18)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: logic system general failure.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x34)
 | |
| 	{
 | |
| 	  as_str = "Scanner error: no dual logic communication.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x05:
 | |
|       /* illegal request */
 | |
|       sense_str = "Illegal request.";
 | |
|       status = SANE_STATUS_INVAL;
 | |
|       if (asc == 0x1a && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Parameter list length error.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x20 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Invalid command operation code.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x24 && ascq == 0x00)
 | |
| 	{
 | |
| 	  /* caught from object_position (via reverse engineering) */
 | |
| 	  /* Not supported? */
 | |
| 	  as_str = "Invalid field in CDB.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x25 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Unsupported LUN.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x26 && ascq == 0x00)
 | |
| 	{
 | |
| 	  /* caught from mode_select (as well as others) */
 | |
| 	  /* Bar/Patch code detection support not installed */
 | |
| 	  /* See Appendix A, Section A.5 */
 | |
| 	  as_str = "Invalid field in parameter list.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x2c && ascq == 0x00)
 | |
| 	{
 | |
| 	  /* we were getting this in read_data during the time
 | |
| 	     that the ADF was misbehaving.  Hopefully we will
 | |
| 	     not see it anymore.
 | |
| 	  */
 | |
| 	  as_str = "Command out of sequence.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x2c && ascq == 0x01)
 | |
| 	{
 | |
| 	  as_str = "Too many windows defined.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x2c && ascq == 0x02)
 | |
| 	{
 | |
| 	  as_str = "Batch start error.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x2c && ascq == 0x03)
 | |
| 	{
 | |
| 	  as_str = "Batch abort error.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x3d && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Invalid bits in IDENTIFY message.";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x06:
 | |
|       /* unit attention */
 | |
|       sense_str = "Unit attention.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       if (asc == 0x04 && ascq == 0x01)
 | |
| 	{
 | |
| 	  as_str = "Reset detected, LUN is becoming ready.";
 | |
| 	  status = SANE_STATUS_DEVICE_BUSY;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x07:
 | |
|       /* data protect */
 | |
|       sense_str = "Data protect.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x08:
 | |
|       /* blank check */
 | |
|       sense_str = "Blank check.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x09:
 | |
|       /* vendor specific */
 | |
|       sense_str = "Vendor specific.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x0A:
 | |
|       /* copy aborted */
 | |
|       sense_str = "Copy aborted.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x0B:
 | |
|       /* aborted command */
 | |
|       sense_str = "Aborted command.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       if (asc == 0x00 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Aborted command (unspecified error).";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x08 && ascq == 0x01)
 | |
| 	{
 | |
| 	  /* caught from start_scan */
 | |
| 	  /* manual feed timeout */
 | |
| 	  as_str = "SCSI Time-out, paper Time-out (SCAN command).";
 | |
| 	  status = SANE_STATUS_NO_DOCS;
 | |
| 	}
 | |
|       else if (asc == 0x47 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "SCSI parity error.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x00)
 | |
| 	{
 | |
| 	  as_str = "Aborted command due to memory error.";
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x01)
 | |
| 	{
 | |
| 	  /* caught from read_data */
 | |
| 	  /* section border error; border is outside the main window */
 | |
| 	  /* See Appendix A, Section A.4 */
 | |
| 	  as_str = "Section Read error (out of border).";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x02)
 | |
| 	{
 | |
| 	  /* caught from read_data */
 | |
| 	  /* No code found; no barcode data is found */
 | |
| 	  /* See Appendix A, Section A.5 */
 | |
| 	  s->barcode_not_found = SANE_TRUE;
 | |
| 	  as_str = "No Bar/Patch Code found.";
 | |
| 	  status = SANE_STATUS_GOOD;
 | |
| 	}
 | |
|       else if (asc == 0x80 && ascq == 0x03)
 | |
| 	{
 | |
| 	  as_str = "Icon Read error (out of border).";
 | |
| 	  status = SANE_STATUS_INVAL;
 | |
| 	}
 | |
|       break;
 | |
|     case 0x0C:
 | |
|       /* equal */
 | |
|       sense_str = "Equal.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x0D:
 | |
|       /* volume overflow */
 | |
|       sense_str = "Volume overflow.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x0E:
 | |
|       /* miscompare */
 | |
|       sense_str = "Miscompare.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     case 0x0F:
 | |
|       /* reserved */
 | |
|       sense_str = "Reserved.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     default:
 | |
|       sense_str = "Unhandled case.";
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   DBG(3, "sense_handler: '%s' '%s' return:%d\n",
 | |
|       sense_str, as_str, status);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| init_options (BH_Scanner * s)
 | |
| {
 | |
|   int i;
 | |
|   DBG (3, "init_options called\n");
 | |
| 
 | |
|   memset (s->opt, 0, sizeof (s->opt));
 | |
|   memset (s->val, 0, sizeof (s->val));
 | |
| 
 | |
|   for (i = 0; i < NUM_OPTIONS; ++i)
 | |
|     {
 | |
|       s->opt[i].size = sizeof (SANE_Word);
 | |
|       s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | |
|     }
 | |
| 
 | |
|   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].cap = SANE_CAP_SOFT_DETECT;
 | |
|   s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | |
|   s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | |
| 
 | |
|   /* "Scan Mode" group: */
 | |
|   s->opt[OPT_MODE_GROUP].name = "";
 | |
|   s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP;
 | |
|   s->opt[OPT_MODE_GROUP].desc = "";
 | |
|   s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_MODE_GROUP].cap = 0;
 | |
|   s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* Preview: */
 | |
|   s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
 | |
|   s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
 | |
|   s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
 | |
|   s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_PREVIEW].w = 0;
 | |
| 
 | |
|   /* Inquiry */
 | |
|   s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY;
 | |
|   s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY;
 | |
|   s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY;
 | |
|   s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_INQUIRY].size = sizeof(inquiry_data);
 | |
|   s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_INQUIRY].s = strdup(inquiry_data);
 | |
|   s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT;
 | |
| 
 | |
|   /* scan mode */
 | |
|   s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
 | |
|   s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
 | |
|   s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
 | |
|   s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_list);
 | |
|   s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_list;
 | |
|   s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]);
 | |
| 
 | |
|   /* Standard resolutions */
 | |
|   s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | |
|   s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | |
|   s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | |
|   s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
 | |
|   s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | |
|   s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList;
 | |
|   s->val[OPT_RESOLUTION].w = s->hw->info.res_default;
 | |
| 
 | |
|   /* compression */
 | |
|   s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION;
 | |
|   s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION;
 | |
|   s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION;
 | |
|   s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_COMPRESSION].size = max_string_size (compression_list);
 | |
|   s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_COMPRESSION].constraint.string_list = compression_list;
 | |
|   s->val[OPT_COMPRESSION].s = strdup (compression_list[0]);
 | |
| 
 | |
|   if (s->hw->info.colorHalftone == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_min_list);
 | |
|       s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_min_list;
 | |
|     }
 | |
| 
 | |
|   if (s->hw->info.comprG3_1D == SANE_FALSE ||
 | |
|       s->hw->info.comprG3_2D == SANE_FALSE ||
 | |
|       s->hw->info.comprG4 == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   /* "Geometry" group: */
 | |
|   s->opt[OPT_GEOMETRY_GROUP].name = "";
 | |
|   s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | |
|   s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].cap = 0;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* Autoborder: */
 | |
|   s->opt[OPT_AUTOBORDER].name = SANE_NAME_AUTOBORDER;
 | |
|   s->opt[OPT_AUTOBORDER].title = SANE_TITLE_AUTOBORDER;
 | |
|   s->opt[OPT_AUTOBORDER].desc = SANE_DESC_AUTOBORDER;
 | |
|   s->opt[OPT_AUTOBORDER].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_AUTOBORDER].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_AUTOBORDER].w = s->hw->info.autoborder_default;
 | |
| 
 | |
|   /* Paper Size */
 | |
|   s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
 | |
|   s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
 | |
|   s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
 | |
|   s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list);
 | |
|   s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_PAPER_SIZE].constraint.string_list = paper_list;
 | |
|   s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]);
 | |
| 
 | |
|   /* rotation */
 | |
|   s->opt[OPT_ROTATION].name = SANE_NAME_ROTATION;
 | |
|   s->opt[OPT_ROTATION].title = SANE_TITLE_ROTATION;
 | |
|   s->opt[OPT_ROTATION].desc = SANE_DESC_ROTATION;
 | |
|   s->opt[OPT_ROTATION].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_ROTATION].size = max_string_size (rotation_list);
 | |
|   s->opt[OPT_ROTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_ROTATION].constraint.string_list = rotation_list;
 | |
|   s->val[OPT_ROTATION].s = strdup (rotation_list[0]);
 | |
| 
 | |
|   /* Deskew: */
 | |
|   s->opt[OPT_DESKEW].name = SANE_NAME_DESKEW;
 | |
|   s->opt[OPT_DESKEW].title = SANE_TITLE_DESKEW;
 | |
|   s->opt[OPT_DESKEW].desc = SANE_DESC_DESKEW;
 | |
|   s->opt[OPT_DESKEW].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_DESKEW].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_DESKEW].w =  s->hw->info.deskew_default;
 | |
| 
 | |
|   /* top-left x */
 | |
|   s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | |
|   s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | |
|   s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | |
|   s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
 | |
|   s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
 | |
|   s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TL_X].constraint.range = &(s->hw->info.x_range);
 | |
|   s->val[OPT_TL_X].w = SANE_FIX(0.0);
 | |
| 
 | |
|   /* top-left y */
 | |
|   s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | |
|   s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | |
|   s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | |
|   s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
 | |
|   s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
 | |
|   s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TL_Y].constraint.range = &(s->hw->info.y_range);
 | |
|   s->val[OPT_TL_Y].w = SANE_FIX(0.0);
 | |
| 
 | |
|   /* bottom-right x */
 | |
|   s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | |
|   s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | |
|   s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | |
|   s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
 | |
|   s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
 | |
|   s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BR_X].constraint.range = &(s->hw->info.x_range);
 | |
|   s->val[OPT_BR_X].w = s->hw->info.x_range.max;
 | |
| 
 | |
|   /* bottom-right y */
 | |
|   s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | |
|   s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | |
|   s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | |
|   s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
 | |
|   s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
 | |
|   s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BR_Y].constraint.range = &(s->hw->info.y_range);
 | |
|   s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
 | |
| 
 | |
|   if (s->hw->info.canBorderRecog == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_AUTOBORDER].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   /* "Feeder" group: */
 | |
|   s->opt[OPT_FEEDER_GROUP].name = "";
 | |
|   s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP;
 | |
|   s->opt[OPT_FEEDER_GROUP].desc = "";
 | |
|   s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED;
 | |
|   s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* scan source */
 | |
|   s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
 | |
|   s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
 | |
|   s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
 | |
|   s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list);
 | |
|   s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_SCAN_SOURCE].constraint.string_list = scan_source_list;
 | |
|   s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]);
 | |
| 
 | |
|   /* Batch: */
 | |
|   s->opt[OPT_BATCH].name = SANE_NAME_BATCH;
 | |
|   s->opt[OPT_BATCH].title = SANE_TITLE_BATCH;
 | |
|   s->opt[OPT_BATCH].desc = SANE_DESC_BATCH;
 | |
|   s->opt[OPT_BATCH].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_BATCH].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_BATCH].w =  s->hw->info.batch_default;
 | |
| 
 | |
|   /* Check ADF: */
 | |
|   s->opt[OPT_CHECK_ADF].name = SANE_NAME_CHECK_ADF;
 | |
|   s->opt[OPT_CHECK_ADF].title = SANE_TITLE_CHECK_ADF;
 | |
|   s->opt[OPT_CHECK_ADF].desc = SANE_DESC_CHECK_ADF;
 | |
|   s->opt[OPT_CHECK_ADF].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_CHECK_ADF].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_CHECK_ADF].w =  s->hw->info.check_adf_default;
 | |
| 
 | |
|   /* Duplex: */
 | |
|   s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
 | |
|   s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
 | |
|   s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
 | |
|   s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_DUPLEX].w = s->hw->info.duplex_default;
 | |
| 
 | |
|   /* timeout adf */
 | |
|   s->opt[OPT_TIMEOUT_ADF].name = SANE_NAME_TIMEOUT_ADF;
 | |
|   s->opt[OPT_TIMEOUT_ADF].title = SANE_TITLE_TIMEOUT_ADF;
 | |
|   s->opt[OPT_TIMEOUT_ADF].desc = SANE_DESC_TIMEOUT_ADF;
 | |
|   s->opt[OPT_TIMEOUT_ADF].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_TIMEOUT_ADF].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_TIMEOUT_ADF].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TIMEOUT_ADF].constraint.range = &u8_range;
 | |
|   s->val[OPT_TIMEOUT_ADF].w =  s->hw->info.timeout_adf_default;
 | |
| 
 | |
|   /* timeout manual */
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].name = SANE_NAME_TIMEOUT_MANUAL;
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].title = SANE_TITLE_TIMEOUT_MANUAL;
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].desc = SANE_DESC_TIMEOUT_MANUAL;
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TIMEOUT_MANUAL].constraint.range = &u8_range;
 | |
|   s->val[OPT_TIMEOUT_MANUAL].w =  s->hw->info.timeout_manual_default;
 | |
| 
 | |
|   if (s->hw->info.canCheckADF == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_CHECK_ADF].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   if (s->hw->info.canDuplex == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   if (s->hw->info.canADF == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_TIMEOUT_ADF].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   /* "Enhancement" group: */
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].name = "";
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* Control Panel: */
 | |
|   s->opt[OPT_CONTROL_PANEL].name = SANE_NAME_CONTROL_PANEL;
 | |
|   s->opt[OPT_CONTROL_PANEL].title = SANE_TITLE_CONTROL_PANEL;
 | |
|   s->opt[OPT_CONTROL_PANEL].desc = SANE_DESC_CONTROL_PANEL;
 | |
|   s->opt[OPT_CONTROL_PANEL].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_CONTROL_PANEL].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_CONTROL_PANEL].w =  s->hw->info.control_panel_default;
 | |
| 
 | |
|   /* Ace_Function */
 | |
|   s->opt[OPT_ACE_FUNCTION].name = SANE_NAME_ACE_FUNCTION;
 | |
|   s->opt[OPT_ACE_FUNCTION].title = SANE_TITLE_ACE_FUNCTION;
 | |
|   s->opt[OPT_ACE_FUNCTION].desc = SANE_DESC_ACE_FUNCTION;
 | |
|   s->opt[OPT_ACE_FUNCTION].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_ACE_FUNCTION].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_ACE_FUNCTION].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_ACE_FUNCTION].constraint.range = &ace_function_range;
 | |
|   s->val[OPT_ACE_FUNCTION].w = 0;
 | |
| 
 | |
|   /* Ace_Sensitivity */
 | |
|   s->opt[OPT_ACE_SENSITIVITY].name = SANE_NAME_ACE_SENSITIVITY;
 | |
|   s->opt[OPT_ACE_SENSITIVITY].title = SANE_TITLE_ACE_SENSITIVITY;
 | |
|   s->opt[OPT_ACE_SENSITIVITY].desc = SANE_DESC_ACE_SENSITIVITY;
 | |
|   s->opt[OPT_ACE_SENSITIVITY].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_ACE_SENSITIVITY].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_ACE_SENSITIVITY].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_ACE_SENSITIVITY].constraint.range = &ace_sensitivity_range;
 | |
|   s->val[OPT_ACE_SENSITIVITY].w = 4;
 | |
| 
 | |
|   /* Brightness */
 | |
|   s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
 | |
|   s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
 | |
|   s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | |
|   s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range;
 | |
|   s->val[OPT_BRIGHTNESS].w = 0;
 | |
| 
 | |
|   /* Threshold */
 | |
|   s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
 | |
|   s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
 | |
|   s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
 | |
|   s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
 | |
|   s->val[OPT_THRESHOLD].w = 0;
 | |
| 
 | |
|   /* Contrast */
 | |
|   s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
 | |
|   s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
 | |
|   s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
 | |
|   s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_CONTRAST].constraint.range = &u8_range;
 | |
|   s->val[OPT_CONTRAST].w = 0;
 | |
| 
 | |
|   /* Negative: */
 | |
|   s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
 | |
|   s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
 | |
|   s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
 | |
|   s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_NEGATIVE].w = SANE_FALSE;
 | |
| 
 | |
|   /* Contrast is not used in any case; why did we add it? */
 | |
|   s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | |
|   if (s->hw->info.control_panel_default == SANE_TRUE)
 | |
|     {
 | |
|       s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
|   else if (s->hw->info.canACE == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   /* "ICON" group: */
 | |
|   s->opt[OPT_ICON_GROUP].name = "";
 | |
|   s->opt[OPT_ICON_GROUP].title = SANE_TITLE_ICON_GROUP;
 | |
|   s->opt[OPT_ICON_GROUP].desc = "";
 | |
|   s->opt[OPT_ICON_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_ICON_GROUP].cap = SANE_CAP_ADVANCED;
 | |
|   s->opt[OPT_ICON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* Icon_Width */
 | |
|   s->opt[OPT_ICON_WIDTH].name = SANE_NAME_ICON_WIDTH;
 | |
|   s->opt[OPT_ICON_WIDTH].title = SANE_TITLE_ICON_WIDTH;
 | |
|   s->opt[OPT_ICON_WIDTH].desc = SANE_DESC_ICON_WIDTH;
 | |
|   s->opt[OPT_ICON_WIDTH].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_ICON_WIDTH].unit = SANE_UNIT_PIXEL;
 | |
|   s->opt[OPT_ICON_WIDTH].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_ICON_WIDTH].constraint.range = &icon_range;
 | |
|   s->val[OPT_ICON_WIDTH].w = 0;
 | |
| 
 | |
|   /* Icon_Length */
 | |
|   s->opt[OPT_ICON_LENGTH].name = SANE_NAME_ICON_LENGTH;
 | |
|   s->opt[OPT_ICON_LENGTH].title = SANE_TITLE_ICON_LENGTH;
 | |
|   s->opt[OPT_ICON_LENGTH].desc = SANE_DESC_ICON_LENGTH;
 | |
|   s->opt[OPT_ICON_LENGTH].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_ICON_LENGTH].unit = SANE_UNIT_PIXEL;
 | |
|   s->opt[OPT_ICON_LENGTH].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_ICON_LENGTH].constraint.range = &icon_range;
 | |
|   s->val[OPT_ICON_LENGTH].w = 0;
 | |
| 
 | |
|   if (s->hw->info.canIcon == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_ICON_GROUP].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_ICON_WIDTH].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_ICON_LENGTH].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   /* "Barcode" group: */
 | |
|   s->opt[OPT_BARCODE_GROUP].name = "";
 | |
|   s->opt[OPT_BARCODE_GROUP].title = SANE_TITLE_BARCODE_GROUP;
 | |
|   s->opt[OPT_BARCODE_GROUP].desc = "";
 | |
|   s->opt[OPT_BARCODE_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_BARCODE_GROUP].cap = SANE_CAP_ADVANCED;
 | |
|   s->opt[OPT_BARCODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* Add <name> to barcode search priority. */
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].name = SANE_NAME_BARCODE_SEARCH_BAR;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].title = SANE_TITLE_BARCODE_SEARCH_BAR;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].desc = SANE_DESC_BARCODE_SEARCH_BAR;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].constraint.string_list = barcode_search_bar_list;
 | |
|   s->opt[OPT_BARCODE_SEARCH_BAR].size = max_string_size (barcode_search_bar_list);
 | |
|   s->val[OPT_BARCODE_SEARCH_BAR].s = strdup (barcode_search_bar_list[0]);
 | |
| 
 | |
|   /* Barcode search count (1-7, default 1). */
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].name = SANE_NAME_BARCODE_SEARCH_COUNT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].title = SANE_TITLE_BARCODE_SEARCH_COUNT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].desc = SANE_DESC_BARCODE_SEARCH_COUNT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_COUNT].constraint.range = &barcode_search_count_range;
 | |
|   s->val[OPT_BARCODE_SEARCH_COUNT].w =  3;
 | |
| 
 | |
|   /* Barcode search mode. horiz-vert, horizontal, vertical, vert-horiz */
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].name = SANE_NAME_BARCODE_SEARCH_MODE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].title = SANE_TITLE_BARCODE_SEARCH_MODE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].desc = SANE_DESC_BARCODE_SEARCH_MODE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].size = max_string_size (barcode_search_mode_list);
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_BARCODE_SEARCH_MODE].constraint.string_list = barcode_search_mode_list;
 | |
|   s->val[OPT_BARCODE_SEARCH_MODE].s = strdup(barcode_search_mode_list[0]);
 | |
| 
 | |
|   /* Patch code min height (def=5mm) */
 | |
|   s->opt[OPT_BARCODE_HMIN].name = SANE_NAME_BARCODE_HMIN;
 | |
|   s->opt[OPT_BARCODE_HMIN].title = SANE_TITLE_BARCODE_HMIN;
 | |
|   s->opt[OPT_BARCODE_HMIN].desc = SANE_DESC_BARCODE_HMIN;
 | |
|   s->opt[OPT_BARCODE_HMIN].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_HMIN].unit = SANE_UNIT_MM;
 | |
|   s->opt[OPT_BARCODE_HMIN].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_HMIN].constraint.range = &barcode_hmin_range;
 | |
|   s->val[OPT_BARCODE_HMIN].w =  5;
 | |
| 
 | |
|   /* Barcode search timeout in ms (20-65535,default is 10000). */
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].name = SANE_NAME_BARCODE_SEARCH_TIMEOUT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].title = SANE_TITLE_BARCODE_SEARCH_TIMEOUT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].desc = SANE_DESC_BARCODE_SEARCH_TIMEOUT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].unit = SANE_UNIT_MICROSECOND;
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint.range = &barcode_search_timeout_range;
 | |
|   s->val[OPT_BARCODE_SEARCH_TIMEOUT].w =  10000;
 | |
| 
 | |
|   /* Specify image sections and functions */
 | |
|   s->opt[OPT_SECTION].name = SANE_NAME_SECTION;
 | |
|   s->opt[OPT_SECTION].title = SANE_TITLE_SECTION;
 | |
|   s->opt[OPT_SECTION].desc = SANE_DESC_SECTION;
 | |
|   s->opt[OPT_SECTION].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_SECTION].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_SECTION].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->opt[OPT_SECTION].size = 255;
 | |
|   s->val[OPT_SECTION].s = strdup ("");
 | |
| 
 | |
|   /* Barcode_Relmax */
 | |
|   s->opt[OPT_BARCODE_RELMAX].name = SANE_NAME_BARCODE_RELMAX;
 | |
|   s->opt[OPT_BARCODE_RELMAX].title = SANE_TITLE_BARCODE_RELMAX;
 | |
|   s->opt[OPT_BARCODE_RELMAX].desc = SANE_DESC_BARCODE_RELMAX;
 | |
|   s->opt[OPT_BARCODE_RELMAX].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_RELMAX].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_RELMAX].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_RELMAX].constraint.range = &u8_range;
 | |
|   s->val[OPT_BARCODE_RELMAX].w = 0;
 | |
| 
 | |
|   /* Barcode_Barmin */
 | |
|   s->opt[OPT_BARCODE_BARMIN].name = SANE_NAME_BARCODE_BARMIN;
 | |
|   s->opt[OPT_BARCODE_BARMIN].title = SANE_TITLE_BARCODE_BARMIN;
 | |
|   s->opt[OPT_BARCODE_BARMIN].desc = SANE_DESC_BARCODE_BARMIN;
 | |
|   s->opt[OPT_BARCODE_BARMIN].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_BARMIN].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_BARMIN].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_BARMIN].constraint.range = &u8_range;
 | |
|   s->val[OPT_BARCODE_BARMIN].w = 0;
 | |
| 
 | |
|   /* Barcode_Barmax */
 | |
|   s->opt[OPT_BARCODE_BARMAX].name = SANE_NAME_BARCODE_BARMAX;
 | |
|   s->opt[OPT_BARCODE_BARMAX].title = SANE_TITLE_BARCODE_BARMAX;
 | |
|   s->opt[OPT_BARCODE_BARMAX].desc = SANE_DESC_BARCODE_BARMAX;
 | |
|   s->opt[OPT_BARCODE_BARMAX].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_BARMAX].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_BARMAX].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_BARMAX].constraint.range = &u8_range;
 | |
|   s->val[OPT_BARCODE_BARMAX].w = 0;
 | |
| 
 | |
|   /* Barcode_Contrast */
 | |
|   s->opt[OPT_BARCODE_CONTRAST].name = SANE_NAME_BARCODE_CONTRAST;
 | |
|   s->opt[OPT_BARCODE_CONTRAST].title = SANE_TITLE_BARCODE_CONTRAST;
 | |
|   s->opt[OPT_BARCODE_CONTRAST].desc = SANE_DESC_BARCODE_CONTRAST;
 | |
|   s->opt[OPT_BARCODE_CONTRAST].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_CONTRAST].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_CONTRAST].constraint.range = &barcode_contrast_range;
 | |
|   s->val[OPT_BARCODE_CONTRAST].w = 3;
 | |
| 
 | |
|   /* Barcode_Patchmode */
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].name = SANE_NAME_BARCODE_PATCHMODE;
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].title = SANE_TITLE_BARCODE_PATCHMODE;
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].desc = SANE_DESC_BARCODE_PATCHMODE;
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BARCODE_PATCHMODE].constraint.range = &barcode_patchmode_range;
 | |
|   s->val[OPT_BARCODE_PATCHMODE].w = 0;
 | |
| 
 | |
|   if (s->hw->info.canSection == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_SECTION].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   if (s->hw->info.canBarCode == SANE_FALSE)
 | |
|     {
 | |
|       s->opt[OPT_BARCODE_GROUP].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_SEARCH_BAR].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_SEARCH_COUNT].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_SEARCH_MODE].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_HMIN].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_SEARCH_TIMEOUT].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_RELMAX].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_BARMIN].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_BARMAX].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_BARCODE_PATCHMODE].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| attach (const char *devnam, BH_Device ** devp)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   BH_Device *dev;
 | |
|   struct inquiry_standard_data ibuf;
 | |
|   struct inquiry_vpd_data vbuf;
 | |
|   struct inquiry_jis_data jbuf;
 | |
|   size_t buf_size;
 | |
|   int fd = -1;
 | |
|   double mm;
 | |
| 
 | |
|   DBG (3, "attach called\n");
 | |
| 
 | |
|   for (dev = first_dev; dev; dev = dev->next)
 | |
|     {
 | |
|       if (strcmp (dev->sane.name, devnam) == 0)
 | |
|         {
 | |
|           if (devp)
 | |
|             *devp = dev;
 | |
|           return SANE_STATUS_GOOD;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| #ifdef FAKE_INQUIRY
 | |
|   if (fake_inquiry)
 | |
|     {
 | |
|       DBG (3, "attach: faking inquiry of %s\n", devnam);
 | |
| 
 | |
|       memset (&ibuf, 0, sizeof (ibuf));
 | |
|       ibuf.devtype = 6;
 | |
|       memcpy(ibuf.vendor, "**FAKE**", 8);
 | |
|       memcpy(ibuf.product, "COPISCAN II 6338", 16);
 | |
|       memcpy(ibuf.revision, "0016", 4);
 | |
| 
 | |
|       DBG (1, "attach: reported devtype='%d', vendor='%.8s', "
 | |
| 	   "product='%.16s', revision='%.4s'\n",
 | |
| 	   ibuf.devtype, ibuf.vendor,
 | |
| 	   ibuf.product, ibuf.revision);
 | |
| 
 | |
|       memset (&vbuf, 0, sizeof (vbuf));
 | |
|       memset (&jbuf, 0, sizeof (jbuf));
 | |
|     }
 | |
|   else
 | |
| #endif
 | |
|     {
 | |
|       DBG (3, "attach: opening %s\n", devnam);
 | |
|       status = sanei_scsi_open (devnam, &fd, sense_handler, NULL);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG (3, "attach: sending TEST_UNIT_READY\n");
 | |
|       status = test_unit_ready (fd);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "attach: test unit ready failed (%s)\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  sanei_scsi_close (fd);
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG (3, "attach: sending INQUIRY (standard data)\n");
 | |
|       memset (&ibuf, 0, sizeof (ibuf));
 | |
|       buf_size = sizeof(ibuf);
 | |
|       status = inquiry (fd, &ibuf, &buf_size, 0,
 | |
| 			BH_INQUIRY_STANDARD_PAGE_CODE);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "attach: inquiry (standard data) failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  sanei_scsi_close (fd);
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG (1, "attach: reported devtype='%d', vendor='%.8s', "
 | |
| 	   "product='%.16s', revision='%.4s'\n",
 | |
| 	   ibuf.devtype, ibuf.vendor,
 | |
| 	   ibuf.product, ibuf.revision);
 | |
| 
 | |
|       if (ibuf.devtype != 6
 | |
| 	  || strncmp ((char *)ibuf.vendor, "B&H SCSI", 8) != 0
 | |
| 	  || strncmp ((char *)ibuf.product, "COPISCAN ", 9) != 0)
 | |
| 	{
 | |
| 	  DBG (1,
 | |
| 	       "attach: device is not a recognized Bell and Howell scanner\n");
 | |
| 	  sanei_scsi_close (fd);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
| 
 | |
|       DBG (3, "attach: sending INQUIRY (vpd data)\n");
 | |
|       memset (&vbuf, 0, sizeof (vbuf));
 | |
|       buf_size = sizeof(vbuf);
 | |
|       status = inquiry (fd, &vbuf, &buf_size, 1,
 | |
| 			BH_INQUIRY_VPD_PAGE_CODE);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "attach: inquiry (vpd data) failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  sanei_scsi_close (fd);
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       DBG (3, "attach: sending INQUIRY (jis data)\n");
 | |
|       memset (&jbuf, 0, sizeof (jbuf));
 | |
|       buf_size = sizeof(jbuf);
 | |
|       status = inquiry (fd, &jbuf, &buf_size, 1,
 | |
| 			BH_INQUIRY_JIS_PAGE_CODE);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "attach: inquiry (jis data) failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  sanei_scsi_close (fd);
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       sanei_scsi_close (fd);
 | |
|     }
 | |
| 
 | |
|   dev = malloc (sizeof (*dev));
 | |
|   if (!dev)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
|   memset (dev, 0, sizeof (*dev));
 | |
| 
 | |
| 
 | |
|   dev->info.devtype = ibuf.devtype;
 | |
|   sprintf(dev->info.vendor, "%.8s", ibuf.vendor);
 | |
|   trim_spaces(dev->info.vendor, sizeof(dev->info.vendor));
 | |
|   sprintf(dev->info.product, "%.16s", ibuf.product);
 | |
|   trim_spaces(dev->info.product, sizeof(dev->info.product));
 | |
|   sprintf(dev->info.revision, "%.4s", ibuf.revision);
 | |
|   trim_spaces(dev->info.revision, sizeof(dev->info.revision));
 | |
| 
 | |
|   dev->sane.name = strdup (devnam);
 | |
|   dev->sane.vendor = strdup(dev->info.vendor);
 | |
|   dev->sane.model = strdup(dev->info.product);;
 | |
|   dev->sane.type = strdup(print_devtype(dev->info.devtype));
 | |
| 
 | |
|   /* set capabilities from vpd */
 | |
|   dev->info.canADF = vbuf.adf & 0x01;
 | |
|   dev->info.colorBandW = vbuf.imagecomposition & 0x01;
 | |
|   dev->info.colorHalftone = vbuf.imagecomposition & 0x02;
 | |
|   dev->info.canWhiteFrame = vbuf.imagedataprocessing[1] & 0x01;
 | |
|   dev->info.canBlackFrame = vbuf.imagedataprocessing[1] & 0x02;
 | |
|   dev->info.canEdgeExtract = vbuf.imagedataprocessing[1] & 0x04;
 | |
|   dev->info.canNoiseFilter = vbuf.imagedataprocessing[1] & 0x08;
 | |
|   dev->info.canSmooth = vbuf.imagedataprocessing[1] & 0x10;
 | |
|   dev->info.canLineBold = vbuf.imagedataprocessing[1] & 0x20;
 | |
|   dev->info.comprG3_1D = vbuf.compression & 0x01;
 | |
|   dev->info.comprG3_2D = vbuf.compression & 0x02;
 | |
|   dev->info.comprG4 = vbuf.compression & 0x04;
 | |
|   dev->info.canBorderRecog = vbuf.sizerecognition & 0x01;
 | |
|   dev->info.canBarCode = vbuf.optionalfeatures & 0x01;
 | |
|   dev->info.canIcon = vbuf.optionalfeatures & 0x02;
 | |
|   dev->info.canSection = vbuf.optionalfeatures & 0x04;
 | |
|   dev->info.lineMaxBytes = _2btol(vbuf.xmaxoutputbytes);
 | |
| 
 | |
| #ifdef FAKE_INQUIRY
 | |
|   if (fake_inquiry)
 | |
|     {
 | |
|       dev->info.canADF = SANE_FALSE;
 | |
|       dev->info.colorBandW = SANE_TRUE;
 | |
|       dev->info.colorHalftone = SANE_TRUE;
 | |
|       dev->info.canWhiteFrame = SANE_TRUE;
 | |
|       dev->info.canBlackFrame = SANE_TRUE;
 | |
|       dev->info.canEdgeExtract = SANE_TRUE;
 | |
|       dev->info.canNoiseFilter = SANE_TRUE;
 | |
|       dev->info.canSmooth = SANE_TRUE;
 | |
|       dev->info.canLineBold = SANE_TRUE;
 | |
|       dev->info.comprG3_1D = SANE_TRUE;
 | |
|       dev->info.comprG3_2D = SANE_TRUE;
 | |
|       dev->info.comprG4 = SANE_TRUE;
 | |
|       dev->info.canBorderRecog = SANE_TRUE;
 | |
|       dev->info.canBarCode = SANE_TRUE;
 | |
|       dev->info.canIcon = SANE_TRUE;
 | |
|       dev->info.canSection = SANE_TRUE;
 | |
|       dev->info.lineMaxBytes = 450;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   /* set capabilities from jis */
 | |
|   dev->info.resBasicX = _2btol(jbuf.basicxres);
 | |
|   dev->info.resBasicY = _2btol(jbuf.basicyres);
 | |
|   dev->info.resMaxX = _2btol(jbuf.maxxres);
 | |
|   dev->info.resMaxY = _2btol(jbuf.maxyres);
 | |
|   dev->info.resMinX = _2btol(jbuf.minxres);
 | |
|   dev->info.resMinY = _2btol(jbuf.minyres);
 | |
| 
 | |
|   /* set the length of the list to zero first, then append standard resolutions */
 | |
|   dev->info.resStdList[0] = 0;
 | |
|   if (jbuf.standardres[0] & 0x80) appendStdList(&dev->info, 60);
 | |
|   if (jbuf.standardres[0] & 0x40) appendStdList(&dev->info, 75);
 | |
|   if (jbuf.standardres[0] & 0x20) appendStdList(&dev->info, 100);
 | |
|   if (jbuf.standardres[0] & 0x10) appendStdList(&dev->info, 120);
 | |
|   if (jbuf.standardres[0] & 0x08) appendStdList(&dev->info, 150);
 | |
|   if (jbuf.standardres[0] & 0x04) appendStdList(&dev->info, 160);
 | |
|   if (jbuf.standardres[0] & 0x02) appendStdList(&dev->info, 180);
 | |
|   if (jbuf.standardres[0] & 0x01) appendStdList(&dev->info, 200);
 | |
|   if (jbuf.standardres[1] & 0x80) appendStdList(&dev->info, 240);
 | |
|   if (jbuf.standardres[1] & 0x40) appendStdList(&dev->info, 300);
 | |
|   if (jbuf.standardres[1] & 0x20) appendStdList(&dev->info, 320);
 | |
|   if (jbuf.standardres[1] & 0x10) appendStdList(&dev->info, 400);
 | |
|   if (jbuf.standardres[1] & 0x08) appendStdList(&dev->info, 480);
 | |
|   if (jbuf.standardres[1] & 0x04) appendStdList(&dev->info, 600);
 | |
|   if (jbuf.standardres[1] & 0x02) appendStdList(&dev->info, 800);
 | |
|   if (jbuf.standardres[1] & 0x01) appendStdList(&dev->info, 1200);
 | |
|   if (dev->info.resStdList[0] == 0)
 | |
|     {
 | |
|       /* make a default standard resolutions for 200 and 300dpi */
 | |
|       DBG(1, "attach: no standard resolutions reported\n");
 | |
|       dev->info.resStdList[0] = 2;
 | |
|       dev->info.resStdList[1] = 200;
 | |
|       dev->info.resStdList[2] = 300;
 | |
|       dev->info.resBasicX = dev->info.resBasicY = 300;
 | |
|     }
 | |
| 
 | |
|   dev->info.winWidth = _4btol(jbuf.windowwidth);
 | |
|   dev->info.winHeight = _4btol(jbuf.windowlength);
 | |
| 
 | |
|   if (dev->info.winWidth <= 0)
 | |
|     {
 | |
|       dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5);
 | |
|       DBG(1, "attach: invalid window width reported, using %d\n", dev->info.winWidth);
 | |
|     }
 | |
|   if (dev->info.winHeight <= 0)
 | |
|     {
 | |
|       dev->info.winHeight = dev->info.resBasicY * 14;
 | |
|       DBG(1, "attach: invalid window height reported, using %d\n", dev->info.winHeight);
 | |
|     }
 | |
| 
 | |
|   mm = (dev->info.resBasicX > 0) ?
 | |
|     ((double) dev->info.winWidth / (double) dev->info.resBasicX * MM_PER_INCH) :
 | |
|     0.0;
 | |
|   dev->info.x_range.min = SANE_FIX(0.0);
 | |
|   dev->info.x_range.max = SANE_FIX(mm);
 | |
|   dev->info.x_range.quant = SANE_FIX(0.0);
 | |
| 
 | |
|   mm = (dev->info.resBasicY > 0) ?
 | |
|     ((double) dev->info.winHeight / (double) dev->info.resBasicY * MM_PER_INCH) :
 | |
|     0.0;
 | |
|   dev->info.y_range.min = SANE_FIX(0.0);
 | |
|   dev->info.y_range.max = SANE_FIX(mm);
 | |
|   dev->info.y_range.quant = SANE_FIX(0.0);
 | |
| 
 | |
|   /* set additional discovered/guessed capabilities */
 | |
| 
 | |
|   /* if all of the ACE capabilities are present, declare it ACE capable */
 | |
|   dev->info.canACE = dev->info.canEdgeExtract &&
 | |
|     dev->info.canNoiseFilter &&
 | |
|     dev->info.canSmooth &&
 | |
|     dev->info.canLineBold;
 | |
| 
 | |
|   /* if the model is known to be a duplex, declare it duplex capable */
 | |
|   if (strcmp(dev->info.product, "COPISCAN II 6338") == 0)
 | |
|     {
 | |
|       dev->info.canDuplex = SANE_TRUE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       dev->info.canDuplex = SANE_FALSE;
 | |
|     }
 | |
| 
 | |
|   /* the paper sensor requires RSC revision 1.4 or higher and an
 | |
|    * installed feeder.  NOTE: It also requires SW-4 on and the
 | |
|    * AccufeedPlus feeder, but we cannot discover that.
 | |
|    */
 | |
|   if (strcmp(dev->info.revision, "0014") >= 0)
 | |
|     {
 | |
|       dev->info.canCheckADF = dev->info.canADF;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       dev->info.canCheckADF = SANE_FALSE;
 | |
|     }
 | |
| 
 | |
|   /* set option defaults based on inquiry information */
 | |
|   dev->info.res_default = dev->info.resBasicX;
 | |
|   dev->info.autoborder_default = dev->info.canBorderRecog;
 | |
|   dev->info.batch_default = SANE_FALSE;
 | |
|   dev->info.deskew_default = SANE_FALSE;
 | |
|   dev->info.check_adf_default = SANE_FALSE;
 | |
|   dev->info.duplex_default = SANE_FALSE;
 | |
|   dev->info.timeout_adf_default = 0;
 | |
|   dev->info.timeout_manual_default = 0;
 | |
|   dev->info.control_panel_default = dev->info.canACE;
 | |
| 
 | |
|   ++num_devices;
 | |
|   dev->next = first_dev;
 | |
|   first_dev = dev;
 | |
| 
 | |
|   if (devp)
 | |
|     *devp = dev;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| attach_one(const char *devnam)
 | |
| {
 | |
|   attach (devnam, NULL);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
 | |
| {
 | |
|     char devnam[PATH_MAX] = "/dev/scanner";
 | |
|     FILE *fp;
 | |
| 
 | |
|     authorize = authorize; /* get rid of compiler warning */
 | |
| 
 | |
|     DBG_INIT();
 | |
|     DBG(3, "sane_init called\n");
 | |
|     DBG(1, "Bell+Howell SANE backend %d.%d build %d %s endian\n",
 | |
| 	SANE_CURRENT_MAJOR, V_MINOR, BUILD,
 | |
| 	_is_host_little_endian() ? "little" : "big");
 | |
| 
 | |
|     if (version_code)
 | |
| 	*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
 | |
| 
 | |
|     fp = sanei_config_open(BH_CONFIG_FILE);
 | |
|     if (fp)
 | |
| 	{
 | |
| 	    char line[PATH_MAX];
 | |
| 	    const char *lp;
 | |
| 	    size_t len;
 | |
| 
 | |
| 	    /* read config file */
 | |
| 	    while (sanei_config_read (line, sizeof (line), fp))
 | |
| 		{
 | |
| 		  if (line[0] == '#')           /* ignore line comments */
 | |
| 		    continue;
 | |
| 		  len = strlen (line);
 | |
| 
 | |
| 		  if (!len)
 | |
| 		    continue;                   /* ignore empty lines */
 | |
| 
 | |
| 		  lp = sanei_config_skip_whitespace (line);
 | |
| 
 | |
| 		  DBG(16,
 | |
| 		      "sane_init: processing config file line '%s'\n",
 | |
| 		      line);
 | |
| 		  if (strncmp(lp, "option", 6) == 0 &&
 | |
| 		      (isspace (lp[6]) || lp[6] == '\0'))
 | |
| 		    {
 | |
| 		      lp += 6;
 | |
| 		      lp = sanei_config_skip_whitespace (lp);
 | |
| 
 | |
| 		      if (strncmp(lp, "disable-optional-frames", 23) == 0)
 | |
| 			{
 | |
| 			  DBG(1, "sane_init: configuration option "
 | |
| 			      "'disable-optional-frames' set\n");
 | |
| 			  disable_optional_frames = 1;
 | |
| 			}
 | |
| 		      else if (strncmp(lp, "fake-inquiry", 12) == 0)
 | |
| 			{
 | |
| 			  DBG(1, "sane_init: configuration option "
 | |
| 			      "'fake-inquiry' set\n");
 | |
| 			  fake_inquiry = 1;
 | |
| 			}
 | |
| 		      else
 | |
| 			{
 | |
| 			  DBG(1, "sane_init: ignoring unknown "
 | |
| 			      "configuration option '%s'\n",
 | |
| 			      lp);
 | |
| 			}
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      DBG(16,
 | |
| 			  "sane_init: found a device: line '%s'\n",
 | |
| 			  lp);
 | |
| 		      strncpy (devnam, lp, sizeof(devnam));
 | |
| 		      devnam[sizeof(devnam)-1] = '\0';
 | |
| 
 | |
| 		      sanei_config_attach_matching_devices(devnam,
 | |
| 							   attach_one);
 | |
| 		    }
 | |
| 		}
 | |
| 	    fclose (fp);
 | |
| 	}
 | |
|     else
 | |
| 	{
 | |
| 	    /* configure the /dev/scanner device in the absence of config file */
 | |
| 	    sanei_config_attach_matching_devices ("/dev/scanner", attach_one);
 | |
| 	}
 | |
| 
 | |
|     return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_devices (const SANE_Device ***device_list, SANE_Bool local)
 | |
| {
 | |
|     BH_Device *dev;
 | |
|     int i;
 | |
|     DBG(3, "sane_get_devices called\n");
 | |
| 
 | |
|     local = local; /* get rid of compiler warning */
 | |
|     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; dev; dev = dev->next)
 | |
| 	devlist[i++] = &dev->sane;
 | |
|     devlist[i++] = 0;
 | |
| 
 | |
|     *device_list = devlist;
 | |
| 
 | |
|     return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_open (SANE_String_Const devnam, SANE_Handle *handle)
 | |
| {
 | |
|     SANE_Status status;
 | |
|     BH_Device *dev;
 | |
|     BH_Scanner *s;
 | |
|     DBG(3, "sane_open called\n");
 | |
| 
 | |
|     if (devnam[0] != '\0')
 | |
| 	{
 | |
| 	    for (dev = first_dev; dev; dev = dev->next)
 | |
| 		{
 | |
| 		    if (strcmp (dev->sane.name, devnam) == 0)
 | |
| 		      break;
 | |
| 		}
 | |
| 
 | |
| 	    if (!dev)
 | |
| 		{
 | |
| 		    status = attach (devnam, &dev);
 | |
| 		    if (status != SANE_STATUS_GOOD)
 | |
| 			return status;
 | |
| 		}
 | |
| 	}
 | |
|     else
 | |
| 	{
 | |
| 	    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->fd = -1;
 | |
|     s->hw = dev;
 | |
| 
 | |
|     s->bmu = BH_UNIT_POINT;
 | |
|     s->mud = 1;
 | |
| 
 | |
|     ScannerDump(s);
 | |
| 
 | |
|     init_options (s);
 | |
| 
 | |
|     s->next = first_handle;
 | |
|     first_handle = s;
 | |
| 
 | |
|     /* initialize our parameters */
 | |
|     get_parameters(s, 0);
 | |
| 
 | |
|     *handle = s;
 | |
| 
 | |
| #ifdef FAKE_INQUIRY
 | |
|     if (fake_inquiry)
 | |
|       {
 | |
| 	DBG (1, "sane_open: faking open of %s\n",
 | |
| 	     s->hw->sane.name);
 | |
|       }
 | |
|     else
 | |
| #endif
 | |
|       {
 | |
| 	status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s);
 | |
| 	if (status != SANE_STATUS_GOOD)
 | |
| 	  {
 | |
| 	    DBG (1, "sane_open: open of %s failed: %s\n",
 | |
| 		 s->hw->sane.name, sane_strstatus (status));
 | |
| 	    return status;
 | |
| 	  }
 | |
|       }
 | |
| 
 | |
|     return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| const SANE_Option_Descriptor *
 | |
| sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | |
| {
 | |
|   BH_Scanner *s = handle;
 | |
|   DBG(3, "sane_get_option_descriptor called (option:%d)\n", option);
 | |
| 
 | |
|   if ((unsigned) option >= NUM_OPTIONS)
 | |
|       return 0;
 | |
| 
 | |
|   return (s->opt + option);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
 | |
| 		     void *val, SANE_Word *info)
 | |
| {
 | |
|   BH_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
|   SANE_Word cap;
 | |
|   SANE_String_Const name;
 | |
| 
 | |
|   DBG(3, "sane_control_option called\n");
 | |
| 
 | |
|   name = s->opt[option].name ? s->opt[option].name : "(nil)";
 | |
| 
 | |
|   if (info)
 | |
|     *info = 0;
 | |
| 
 | |
|   if (s->scanning && action == SANE_ACTION_SET_VALUE)
 | |
|     return SANE_STATUS_DEVICE_BUSY;
 | |
|   if (option >= NUM_OPTIONS)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   cap = s->opt[option].cap;
 | |
|   if (!SANE_OPTION_IS_ACTIVE (cap))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if (action == SANE_ACTION_GET_VALUE)
 | |
|     {
 | |
|       DBG(16, "sane_control_option: get_value %s [#%d]\n", name, 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_TIMEOUT_ADF:
 | |
| 	case OPT_TIMEOUT_MANUAL:
 | |
| 	case OPT_ACE_FUNCTION:
 | |
| 	case OPT_ACE_SENSITIVITY:
 | |
| 	case OPT_BRIGHTNESS:
 | |
| 	case OPT_THRESHOLD:
 | |
| 	case OPT_CONTRAST:
 | |
| 	case OPT_ICON_WIDTH:
 | |
| 	case OPT_ICON_LENGTH:
 | |
| 	case OPT_BARCODE_SEARCH_COUNT:
 | |
| 	case OPT_BARCODE_HMIN:
 | |
| 	case OPT_BARCODE_SEARCH_TIMEOUT:
 | |
| 	case OPT_BARCODE_RELMAX:
 | |
| 	case OPT_BARCODE_BARMIN:
 | |
| 	case OPT_BARCODE_BARMAX:
 | |
| 	case OPT_BARCODE_CONTRAST:
 | |
| 	case OPT_BARCODE_PATCHMODE:
 | |
| 	case OPT_NUM_OPTS:
 | |
| 	  *(SANE_Word *) val = s->val[option].w;
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	  /* string options: */
 | |
| 	case OPT_INQUIRY:
 | |
| 	case OPT_SCAN_SOURCE:
 | |
| 	case OPT_SCAN_MODE:
 | |
| 	case OPT_COMPRESSION:
 | |
| 	case OPT_PAPER_SIZE:
 | |
| 	case OPT_ROTATION:
 | |
| 	case OPT_BARCODE_SEARCH_BAR:
 | |
| 	case OPT_BARCODE_SEARCH_MODE:
 | |
| 	case OPT_SECTION:
 | |
| 	  strcpy (val, s->val[option].s);
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	  /* boolean options: */
 | |
| 	case OPT_PREVIEW:
 | |
| 	case OPT_AUTOBORDER:
 | |
| 	case OPT_DESKEW:
 | |
| 	case OPT_BATCH:
 | |
| 	case OPT_CHECK_ADF:
 | |
| 	case OPT_DUPLEX:
 | |
| 	case OPT_CONTROL_PANEL:
 | |
| 	case OPT_NEGATIVE:
 | |
| 	  *(SANE_Word *) val = s->val[option].w;
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	default:
 | |
| 	  DBG(1, "sane_control_option:invalid option number %d\n", option);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
|     }
 | |
|   else if (action == SANE_ACTION_SET_VALUE)
 | |
|     {
 | |
|       switch (s->opt[option].type)
 | |
| 	{
 | |
| 	case SANE_TYPE_BOOL:
 | |
| 	case SANE_TYPE_INT:
 | |
| 	  DBG(16, "sane_control_option: set_value %s [#%d] to %d\n",
 | |
| 	      name, option, *(SANE_Word *) val);
 | |
| 	  break;
 | |
| 
 | |
| 	case SANE_TYPE_FIXED:
 | |
| 	  DBG(16, "sane_control_option: set_value %s [#%d] to %f\n",
 | |
| 	      name, option, SANE_UNFIX(*(SANE_Word *) val));
 | |
| 	  break;
 | |
| 
 | |
| 	case SANE_TYPE_STRING:
 | |
| 	  DBG(16, "sane_control_option: set_value %s [#%d] to %s\n",
 | |
| 	      name, option, (char *) val);
 | |
| 	  break;
 | |
| 
 | |
| 	default:
 | |
| 	  DBG(16, "sane_control_option: set_value %s [#%d]\n",
 | |
| 	      name, option);
 | |
| 	}
 | |
| 
 | |
|       if (!SANE_OPTION_IS_SETTABLE (cap))
 | |
| 	return SANE_STATUS_INVAL;
 | |
| 
 | |
|       status = sanei_constrain_value (s->opt + option, val, info);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	return status;
 | |
| 
 | |
|       switch (option)
 | |
| 	{
 | |
| 	  /* (mostly) side-effect-free word options: */
 | |
| 	case OPT_TL_X:
 | |
| 	case OPT_TL_Y:
 | |
| 	case OPT_BR_X:
 | |
| 	case OPT_BR_Y:
 | |
| 	  /* make sure that paper-size is set to custom */
 | |
| 	  if (s->val[option].w != *(SANE_Word *) val)
 | |
| 	    {
 | |
| 	      if (info) *info |= SANE_INFO_RELOAD_PARAMS;
 | |
| 
 | |
| 	      if (get_paper_id(_OPT_VAL_STRING(s, OPT_PAPER_SIZE)) != 0)
 | |
| 		{
 | |
| 		  if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
 | |
| 
 | |
| 		  /* set paper size to 'custom' */
 | |
| 		  free (s->val[OPT_PAPER_SIZE].s);
 | |
| 		  s->val[OPT_PAPER_SIZE].s = strdup(paper_list[0]);
 | |
| 		}
 | |
| 	    }
 | |
| 	  /* fall through */
 | |
| 	case OPT_RESOLUTION:
 | |
| 	  if (info && s->val[option].w != *(SANE_Word *) val)
 | |
| 	    *info |= SANE_INFO_RELOAD_PARAMS;
 | |
| 	  /* fall through */
 | |
| 	case OPT_TIMEOUT_ADF:
 | |
| 	case OPT_TIMEOUT_MANUAL:
 | |
| 	case OPT_ACE_FUNCTION:
 | |
| 	case OPT_ACE_SENSITIVITY:
 | |
| 	case OPT_BRIGHTNESS:
 | |
| 	case OPT_THRESHOLD:
 | |
| 	case OPT_CONTRAST:
 | |
| 	case OPT_ICON_WIDTH:
 | |
| 	case OPT_ICON_LENGTH:
 | |
| 	case OPT_BARCODE_SEARCH_COUNT:
 | |
| 	case OPT_BARCODE_HMIN:
 | |
| 	case OPT_BARCODE_SEARCH_TIMEOUT:
 | |
| 	case OPT_BARCODE_RELMAX:
 | |
| 	case OPT_BARCODE_BARMIN:
 | |
| 	case OPT_BARCODE_BARMAX:
 | |
| 	case OPT_BARCODE_CONTRAST:
 | |
| 	case OPT_BARCODE_PATCHMODE:
 | |
| 	case OPT_NUM_OPTS:
 | |
| 	  s->val[option].w = *(SANE_Word *) val;
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	  /* string options */
 | |
| 	case OPT_BARCODE_SEARCH_BAR:
 | |
| 	  /*!!! we're supporting only a single barcode type via the option */
 | |
| 	  s->search_bars[0] = get_barcode_id(val);
 | |
| 	  /* fall through */
 | |
| 	case OPT_SCAN_SOURCE:
 | |
| 	case OPT_COMPRESSION:
 | |
| 	case OPT_ROTATION:
 | |
| 	case OPT_BARCODE_SEARCH_MODE:
 | |
| 	case OPT_SECTION:
 | |
| 	  if (s->val[option].s)
 | |
| 	    free (s->val[option].s);
 | |
| 	  s->val[option].s = strdup (val);
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	  /* boolean options: */
 | |
| 	case OPT_AUTOBORDER:
 | |
| 	  /*!!! autoborder true disables geometry controls
 | |
| 	   * and sets them to defaults?
 | |
| 	   */
 | |
| 	  /* fall through */
 | |
| 	case OPT_PREVIEW:
 | |
| 	case OPT_BATCH:
 | |
| 	case OPT_DESKEW:
 | |
| 	case OPT_CHECK_ADF:
 | |
| 	case OPT_DUPLEX:
 | |
| 	case OPT_NEGATIVE:
 | |
| 	  s->val[option].w = *(SANE_Word *) val;
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	  /* options with side effects */
 | |
| 	case OPT_CONTROL_PANEL:
 | |
| 	  /* a boolean option */
 | |
| 	  /* control-panel true enables/disables some enhancement controls */
 | |
| 	  if (s->val[option].w != *(SANE_Word *) val)
 | |
| 	    {
 | |
| 	      if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
 | |
| 
 | |
| 	      s->val[option].w = *(SANE_Word *) val;
 | |
| 
 | |
| 	      if (*(SANE_Word *) val == SANE_TRUE)
 | |
| 		{
 | |
| 		  if (s->hw->info.canACE == SANE_TRUE)
 | |
| 		    {
 | |
| 		      s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE;
 | |
| 		      s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | |
| 		      s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
| 		    }
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  if (s->hw->info.canACE == SANE_TRUE)
 | |
| 		    {
 | |
| 		      s->opt[OPT_ACE_FUNCTION].cap &= ~SANE_CAP_INACTIVE;
 | |
| 		      s->opt[OPT_ACE_SENSITIVITY].cap &= ~SANE_CAP_INACTIVE;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | |
| 		      s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	case OPT_SCAN_MODE:
 | |
| 	  /* a string option */
 | |
| 	  /* scan mode != lineart disables compression, setting it to
 | |
| 	   * 'none'
 | |
| 	   */
 | |
| 	  if (strcmp (s->val[option].s, (SANE_String) val))
 | |
| 	    {
 | |
| 	      if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
 | |
| 	      if (get_scan_mode_id((SANE_String) val) != 0)
 | |
| 		{
 | |
| 		  /* scan mode is not lineart, disable compression
 | |
| 		   * and set compression to 'none'
 | |
| 		   */
 | |
| 		  s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE;
 | |
| 		  if (s->val[OPT_COMPRESSION].s &&
 | |
| 		      get_compression_id(s->val[OPT_COMPRESSION].s) != 0)
 | |
| 		    {
 | |
| 		      free (s->val[OPT_COMPRESSION].s);
 | |
| 		      s->val[OPT_COMPRESSION].s = strdup(compression_list[0]);
 | |
| 		    }
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  /* scan mode is lineart, enable compression */
 | |
| 		  s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE;
 | |
| 		}
 | |
| 	      free (s->val[option].s);
 | |
| 	      s->val[option].s = strdup (val);
 | |
| 	    }
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	case OPT_PAPER_SIZE:
 | |
| 	  /* a string option */
 | |
| 	  /* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */
 | |
| 	  if (strcmp (s->val[option].s, (SANE_String) val))
 | |
| 	    {
 | |
| 	      SANE_Int paper_id = get_paper_id((SANE_String) val);
 | |
| 
 | |
| 	      /* paper_id 0 is a special case (custom) that
 | |
| 	       * disables the paper size control of geometry
 | |
| 	       */
 | |
| 	      if (paper_id != 0)
 | |
| 		{
 | |
| 		  double left, x_max, y_max, x, y;
 | |
| 
 | |
| 		  x_max = SANE_UNFIX(s->hw->info.x_range.max);
 | |
| 		  y_max = SANE_UNFIX(s->hw->info.y_range.max);
 | |
| 		  /* a dimension of 0.0 (or less) is replaced with the max value */
 | |
| 		  x = (paper_sizes[paper_id].width <= 0.0) ? x_max :
 | |
| 		    paper_sizes[paper_id].width;
 | |
| 		  y = (paper_sizes[paper_id].length <= 0.0) ? y_max :
 | |
| 		    paper_sizes[paper_id].length;
 | |
| 
 | |
| 		  if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
 | |
| 
 | |
| 		  /* set geometry options based on paper size */
 | |
| 		  /* set geometry options based on paper size */
 | |
| 		  if (s->hw->info.canADF)
 | |
| 		    {
 | |
| 		      /* when the feeder is used the paper is centered in the
 | |
| 		       * hopper; with the manual feed it is aligned left.
 | |
| 		       */
 | |
| 		      left = (x_max - x) / 2.0;
 | |
| 		      if (left < 0.0) left = 0.0;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      left = 0.0;
 | |
| 		    }
 | |
| 
 | |
| 		  s->val[OPT_TL_X].w = SANE_FIX(left);
 | |
| 		  s->val[OPT_TL_Y].w = SANE_FIX(0.0);
 | |
| 		  s->val[OPT_BR_X].w = SANE_FIX(MIN(x + left, x_max));
 | |
| 		  s->val[OPT_BR_Y].w = SANE_FIX(MIN(y, y_max));
 | |
| 		}
 | |
| 	      free (s->val[option].s);
 | |
| 	      s->val[option].s = strdup (val);
 | |
| 	    }
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	default:
 | |
| 	  DBG(1, "sane_control_option:invalid option number %d\n", option);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_INVAL;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
 | |
| {
 | |
|   BH_Scanner *s = handle;
 | |
|   SANE_Int status = SANE_STATUS_GOOD;
 | |
| 
 | |
|   DBG(3, "sane_get_parameters called\n");
 | |
| 
 | |
|   if (params)
 | |
|     {
 | |
|       SANE_Int res;
 | |
| 
 | |
|       if (!s->scanning)
 | |
| 	{
 | |
| 	  /* update our parameters ONLY if we're not scanning */
 | |
| 	  status = get_parameters(s, 0);
 | |
| 	}
 | |
| 
 | |
|       *params = s->params;
 | |
| 
 | |
|       res = _OPT_VAL_WORD(s, OPT_RESOLUTION);
 | |
| 
 | |
|       DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, "
 | |
| 	   "lines=%d, dpi=%d\n",
 | |
| 	   (int) s->params.format,
 | |
| 	   s->params.pixels_per_line,
 | |
| 	   s->params.bytes_per_line,
 | |
| 	   s->params.lines,
 | |
| 	   res);
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_start (SANE_Handle handle)
 | |
| {
 | |
|   BH_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG(3, "sane_start called\n");
 | |
|   s->cancelled = SANE_FALSE;
 | |
| 
 | |
|   if (s->scanning == SANE_FALSE)
 | |
|     {
 | |
|       /* get preliminary parameters */
 | |
|       status = get_parameters (s, 0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "sane_start: get_parameters failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       /* Do the setup once per 'batch'.  The SANE standard requires the
 | |
|        * frontend to call sane_cancel once all desired frames have been
 | |
|        * acquired.  That is when scanning is set back to SANE_FALSE and
 | |
|        * the 'batch' is considered done.
 | |
|        */
 | |
|       status = start_setup (s);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (1, "sane_start: start_setup failed: %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   status = start_scan (s);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "sane_start: start_scan failed: %s\n",
 | |
| 	   sane_strstatus (status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
 | |
| {
 | |
|   BH_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
|   size_t nread;
 | |
| 
 | |
|   DBG(3, "sane_read called\n");
 | |
| 
 | |
|   *len = 0;
 | |
| 
 | |
|   if (s->cancelled) {
 | |
|     DBG (3, "sane_read: cancelled!\n");
 | |
|     return SANE_STATUS_CANCELLED;
 | |
|   }
 | |
| 
 | |
|   if (!s->scanning) {
 | |
|     DBG (3, "sane_read: scanning is false!\n");
 | |
|     sane_cancel(s);
 | |
|     return SANE_STATUS_CANCELLED;
 | |
|   }
 | |
| 
 | |
|   nread = maxlen;
 | |
| 
 | |
|   DBG (3, "sane_read: request %lu bytes\n", (u_long) nread);
 | |
|   /* set InvalidBytes to 0 before read; sense_handler will set it
 | |
|    * to non-zero if we do the last partial read.
 | |
|    */
 | |
|   s->InvalidBytes = 0;
 | |
|   status = read_data (s, buf, &nread);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "sane_read: read_data failed %s\n",
 | |
| 	   sane_strstatus(status));
 | |
|       sane_cancel (s);
 | |
|       return status;
 | |
|     }
 | |
|   nread = maxlen - s->InvalidBytes;
 | |
|   DBG (3, "sane_read: got %lu bytes\n", (u_long) nread);
 | |
|   *len = nread;
 | |
| 
 | |
|   return (maxlen != 0 && nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | |
| {
 | |
| #ifdef NONBLOCKSUPPORTED
 | |
|   BH_Scanner *s = handle;
 | |
| #endif
 | |
|   DBG(3, "sane_set_io_mode called: non_blocking=%d\n", non_blocking);
 | |
| 
 | |
| #ifdef NONBLOCKSUPPORTED
 | |
|   if (s->fd < 0)
 | |
|     {
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   if (fcntl (s->fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
 | |
|     {
 | |
|       DBG(1, "sane_set_io_mode: error setting io mode\n");
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|  return SANE_STATUS_GOOD;
 | |
| #else
 | |
|  handle = handle; /* get rid of compiler warning */
 | |
|  return (non_blocking == 1) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
 | |
| {
 | |
| #ifdef NONBLOCKSUPPORTED
 | |
|   BH_Scanner *s = handle;
 | |
| #endif
 | |
|   DBG(3, "sane_get_select_fd called\n");
 | |
| 
 | |
| #ifdef NONBLOCKSUPPORTED
 | |
|   if (s->fd < 0)
 | |
|     {
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
|   *fd = s->fd;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| #else
 | |
|   handle = handle; fd = fd; /* get rid of compiler warning */
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_cancel (SANE_Handle handle)
 | |
| {
 | |
|   BH_Scanner *s = (BH_Scanner *) handle;
 | |
|   DBG(3, "sane_cancel called\n");
 | |
|   if (s->scanning)
 | |
|     {
 | |
|       /* if batchmode is enabled, then call set_window to
 | |
|        * abort the batch
 | |
|        */
 | |
|       if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE)
 | |
| 	{
 | |
| 	  DBG(5, "sane_cancel: calling set_window to abort batch\n");
 | |
| 	  set_window(s, BH_BATCH_ABORT);
 | |
| 	}
 | |
|     }
 | |
|   s->scanning = SANE_FALSE;
 | |
|   s->cancelled = SANE_TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_close (SANE_Handle handle)
 | |
| {
 | |
|   BH_Scanner *s = (BH_Scanner *) handle;
 | |
|   DBG(3, "sane_close called\n");
 | |
| 
 | |
|   if (s->fd != -1)
 | |
|     sanei_scsi_close (s->fd);
 | |
|   s->fd = -1;
 | |
|   free (s);
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_exit (void)
 | |
| {
 | |
|   BH_Device *dev, *next;
 | |
|   DBG(3, "sane_exit called\n");
 | |
| 
 | |
|   for (dev = first_dev; dev; dev = next)
 | |
|     {
 | |
|       next = dev->next;
 | |
|       free (dev);
 | |
|     }
 | |
| 
 | |
|   if (devlist)
 | |
|     free (devlist);
 | |
| }
 |