kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			3718 wiersze
		
	
	
		
			105 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			3718 wiersze
		
	
	
		
			105 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
| 
 | |
|    Copyright (C) 2000-2001 Kazuya Fukuda, based on sharp.c, which is
 | |
|    based on canon.c.
 | |
| 
 | |
|    This file is part of the SANE package.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU General Public License as
 | |
|    published by the Free Software Foundation; either version 2 of the
 | |
|    License, or (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful, but
 | |
|    WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|    MA 02111-1307, USA.
 | |
| 
 | |
|    As a special exception, the authors of SANE give permission for
 | |
|    additional uses of the libraries contained in this release of SANE.
 | |
| 
 | |
|    The exception is that, if you link a SANE library with other files
 | |
|    to produce an executable, this does not by itself cause the
 | |
|    resulting executable to be covered by the GNU General Public
 | |
|    License.  Your use of that executable is in no way restricted on
 | |
|    account of linking the SANE library code into it.
 | |
| 
 | |
|    This exception does not, however, invalidate any other reasons why
 | |
|    the executable file might be covered by the GNU General Public
 | |
|    License.
 | |
| 
 | |
|    If you submit changes to SANE to the maintainers to be included in
 | |
|    a subsequent release, you agree by submitting the changes that
 | |
|    those changes may be distributed with this exception intact.
 | |
| 
 | |
|    If you write modifications of your own for SANE, it is your choice
 | |
|    whether to permit this exception to apply to your modifications.
 | |
|    If you do not wish that, delete this exception notice.
 | |
| 
 | |
|    This file implements a SANE backend for NEC flatbed scanners.  */
 | |
| 
 | |
| /*
 | |
|    Version 0.12
 | |
|    - Remove references to sharp backend (grep for "JX").
 | |
|    - Check for HAVE_SYS_SHM_H before including sys/shm.h and
 | |
|      disable shared memory support if necessary.
 | |
|    - free devlist allocated in sane_get_devices() in sane_exit()
 | |
|    - resolution setting bug fixed(PC-IN500/4C 10dpi step)
 | |
|    - remove resolution list
 | |
|    Version 0.11
 | |
|    - get_data_buffer_status is not called in sane_get_parameter and
 | |
|      sane_read_direct, sane_read_shuffled.
 | |
|    - change some #include <> to ""
 | |
|    Version 0.10
 | |
|    - First release!
 | |
|    - suppoted scanner
 | |
|      PC-IN500/4C                    available
 | |
|      MultiReder 300U/300S series    not available
 | |
|      MultiReder 600U/600S series    not available
 | |
|      MultiReader PetiScan series    not available
 | |
| */
 | |
| #include "../include/sane/config.h"
 | |
| 
 | |
| #include <limits.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdarg.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <errno.h>
 | |
| #include <math.h>
 | |
| 
 | |
| #include "../include/sane/sane.h"
 | |
| #include "../include/sane/saneopts.h"
 | |
| #include "../include/sane/sanei_scsi.h"
 | |
| 
 | |
| /* QUEUEDEBUG should be undefined unless you want to play
 | |
|    with the sanei_scsi.c under Linux and/or with the Linux's SG driver,
 | |
|    or your suspect problems with command queueing
 | |
| */
 | |
| #define QUEUEDEBUG
 | |
| /*#define DEBUG*/
 | |
| #ifdef DEBUG
 | |
| #include <unistd.h>
 | |
| #include <sys/time.h>
 | |
| #endif
 | |
| 
 | |
| /* USE_FORK: fork a special reader process
 | |
|    disable shared memory support.
 | |
| */
 | |
| #if 0
 | |
| #ifdef HAVE_SYS_SHM_H
 | |
| #define USE_FORK
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_FORK
 | |
| #include <signal.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/wait.h>
 | |
| 
 | |
| #include <sys/ipc.h>
 | |
| #include <sys/shm.h>
 | |
| 
 | |
| #endif /* USE_FORK */
 | |
| 
 | |
| #ifndef USE_CUSTOM_GAMMA
 | |
| #define USE_CUSTOM_GAMMA
 | |
| #endif
 | |
| #ifndef USE_COLOR_THRESHOLD
 | |
| #define USE_COLOR_THRESHOLD
 | |
| #endif
 | |
| /* enable a short list of some standard resolutions. XSane provides
 | |
|    its own resolution list; therefore its is generally not reasonable
 | |
|    to enable this list, if you mainly using XSane. But it might be handy
 | |
|    if you are working with xscanimage
 | |
| */
 | |
| /* #define USE_RESOLUTION_LIST */
 | |
| 
 | |
| #define BACKEND_NAME nec
 | |
| #include "../include/sane/sanei_backend.h"
 | |
| 
 | |
| #ifndef PATH_MAX
 | |
| #define PATH_MAX	1024
 | |
| #endif
 | |
| 
 | |
| #define DEFAULT_MUD_1200 1200
 | |
| 
 | |
| #define PIX_TO_MM(x, mud) ((x) * 25.4 / mud)
 | |
| #define MM_TO_PIX(x, mud) ((x) * mud / 25.4)
 | |
| 
 | |
| #include "../include/sane/sanei_config.h"
 | |
| #define NEC_CONFIG_FILE "nec.conf"
 | |
| 
 | |
| #include "nec.h"
 | |
| 
 | |
| static int num_devices = 0;
 | |
| static NEC_Device *first_dev = NULL;
 | |
| static NEC_Scanner *first_handle = NULL;
 | |
| static const SANE_Device **devlist = 0;
 | |
| 
 | |
| typedef enum
 | |
|   {
 | |
|     MODES_LINEART  = 0,
 | |
|     MODES_GRAY,
 | |
|     MODES_COLOR,
 | |
|     MODES_LINEART_COLOR
 | |
|   }
 | |
| Modes;
 | |
| 
 | |
| #define M_LINEART            SANE_VALUE_SCAN_MODE_LINEART
 | |
| #define M_GRAY               SANE_VALUE_SCAN_MODE_GRAY
 | |
| #define M_LINEART_COLOR      "Lineart Color"
 | |
| #define M_COLOR              SANE_VALUE_SCAN_MODE_COLOR
 | |
| static const SANE_String_Const mode_list[] =
 | |
| {
 | |
| #if 0
 | |
|   M_LINEART, M_GRAY, M_LINEART_COLOR, M_COLOR,
 | |
| #endif
 | |
|   M_LINEART, M_GRAY, M_COLOR,
 | |
|   0
 | |
| };
 | |
| 
 | |
| #define M_BILEVEL        "none"
 | |
| #define M_BAYER          "Dither Bayer"
 | |
| #define M_SPIRAL         "Dither Spiral"
 | |
| #define M_DISPERSED      "Dither Dispersed"
 | |
| #define M_ERRDIFFUSION   "Error Diffusion"
 | |
| 
 | |
| #define M_DITHER1        "Dither 1"
 | |
| #define M_DITHER2        "Dither 2"
 | |
| #define M_DITHER3        "Dither 3"
 | |
| #define M_DITHERUSER     "User defined"
 | |
| 
 | |
| static const SANE_String_Const halftone_list[] =
 | |
| {
 | |
|   M_BILEVEL, M_DITHER1, M_DITHER2, M_DITHER3,
 | |
|   0
 | |
| };
 | |
| 
 | |
| #define LIGHT_GREEN "green"
 | |
| #define LIGHT_RED   "red"
 | |
| #define LIGHT_BLUE  "blue"
 | |
| #define LIGHT_NONE  "none"
 | |
| #define LIGHT_WHITE "white"
 | |
| 
 | |
| static const SANE_String_Const light_color_list[] =
 | |
| {
 | |
|   LIGHT_GREEN, LIGHT_RED, LIGHT_BLUE, LIGHT_NONE,
 | |
|   0
 | |
| };
 | |
| 
 | |
| /* possible values for ADF/FSU selection */
 | |
| static SANE_String use_adf = "Automatic Document Feeder";
 | |
| static SANE_String use_fsu = "Transparency Adapter";
 | |
| static SANE_String use_simple = "Flatbed";
 | |
| 
 | |
| #define HAVE_FSU 1
 | |
| #define HAVE_ADF 2
 | |
| 
 | |
| /* The follow #defines are used in NEC_Scanner.adf_fsu_mode
 | |
|    and as indexes for the arrays x_ranges, y_ranges in NEC_Device
 | |
| */
 | |
| #define SCAN_SIMPLE 0
 | |
| #define SCAN_WITH_FSU 1
 | |
| #define SCAN_WITH_ADF 2
 | |
| 
 | |
| #define LOAD_PAPER 1
 | |
| #define UNLOAD_PAPER 0
 | |
| 
 | |
| #define PAPER_MAX  10
 | |
| #define W_LETTER "11\"x17\""
 | |
| #define INVOICE  "8.5\"x5.5\""
 | |
| static const SANE_String_Const paper_list_pcinxxx[] =
 | |
| {
 | |
|   "A3", "A4", "A5", "A6", "B4", "B5",
 | |
|   W_LETTER, "Legal", "Letter", INVOICE,
 | |
|   0
 | |
| };
 | |
| 
 | |
| static const SANE_String_Const paper_list_pcin500[] =
 | |
| {
 | |
|   "A4", "A5", "A6", "B5",
 | |
|   0
 | |
| };
 | |
| 
 | |
| 
 | |
| #define CRT1    "CRT1"
 | |
| #define CRT2    "CRT2"
 | |
| #define PRINTER1 "PRINTER1"
 | |
| #define PRINTER2 "PRINTER2"
 | |
| #define NONE    "NONE"
 | |
| /* #define CUSTOM  "CUSTOM" */
 | |
| static const SANE_String_Const gamma_list[] =
 | |
| {
 | |
|   CRT1, CRT2, PRINTER1, PRINTER2, NONE,
 | |
|   0
 | |
| };
 | |
| 
 | |
| #if 0
 | |
| #define SPEED_NORMAL    "Normal"
 | |
| #define SPEED_FAST      "Fast"
 | |
| static const SANE_String_Const speed_list[] =
 | |
| {
 | |
|   SPEED_NORMAL, SPEED_FAST,
 | |
|   0
 | |
| };
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_RESOLUTION_LIST
 | |
| #define RESOLUTION_MAX_PCINXXX 8
 | |
| static const SANE_String_Const resolution_list_pcinxxx[] =
 | |
| {
 | |
|   "50", "75", "100", "150", "200", "300", "400", "600", "Select",
 | |
|   0
 | |
| };
 | |
| 
 | |
| #define RESOLUTION_MAX_PCIN500 8
 | |
| static const SANE_String_Const resolution_list_pcin500[] =
 | |
| {
 | |
|   "50", "75", "100", "150", "200", "300", "400", "480", "Select",
 | |
|   0
 | |
| };
 | |
| #endif
 | |
| 
 | |
| #define EDGE_NONE    "None"
 | |
| #define EDGE_MIDDLE  "Middle"
 | |
| #define EDGE_STRONG  "Strong"
 | |
| #define EDGE_BLUR    "Blur"
 | |
| static const SANE_String_Const edge_emphasis_list[] =
 | |
| {
 | |
|   EDGE_NONE, EDGE_MIDDLE, EDGE_STRONG, EDGE_BLUR,
 | |
|   0
 | |
| };
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| static const SANE_Range u8_range =
 | |
|   {
 | |
|       0,				/* minimum */
 | |
|     255,				/* maximum */
 | |
|       0				/* quantization */
 | |
|   };
 | |
| #endif
 | |
| 
 | |
| static SANE_Status
 | |
| sense_handler(int fd, u_char *sense_buffer, void *ss)
 | |
| {
 | |
|   int sense_key;
 | |
|   NEC_Sense_Data *sdat = (NEC_Sense_Data *) ss;
 | |
| 
 | |
|   fd = fd; /* silence compilation warnings */
 | |
| 
 | |
|   #define add_sense_code sense_buffer[12]
 | |
|   #define add_sense_qual sense_buffer[13]
 | |
| 
 | |
|   memcpy(sdat->sb, sense_buffer, 16);
 | |
| 
 | |
|   DBG(10, "sense code: %02x %02x %02x %02x %02x %02x %02x %02x "
 | |
|           "%02x %02x %02x %02x %02x %02x %02x %02x\n",
 | |
|           sense_buffer[0], sense_buffer[1], sense_buffer[2], sense_buffer[3],
 | |
|           sense_buffer[4], sense_buffer[5], sense_buffer[6], sense_buffer[7],
 | |
|           sense_buffer[8], sense_buffer[9], sense_buffer[10], sense_buffer[11],
 | |
|           sense_buffer[12], sense_buffer[13], sense_buffer[14], sense_buffer[15]);
 | |
| 
 | |
|   sense_key = sense_buffer[1] & 0x0F;
 | |
|   /* do we have additional information ? */
 | |
|   if (sense_buffer[7] >= 5)
 | |
|     {
 | |
|       if (sdat->model == PCIN500)
 | |
|         {
 | |
|           switch (sense_key)
 | |
|             {
 | |
|               case 0x02: /* not ready */
 | |
|                 switch (add_sense_code)
 | |
|                   {
 | |
|                     case 0x80:
 | |
|                       switch (add_sense_qual & 0xf0)
 | |
|                         {
 | |
|                           case 0x10:
 | |
|                             DBG(1, "Scanner not ready: memory error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x20:
 | |
|                             DBG(1, "Scanner not ready: hardware error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x30:
 | |
|                             DBG(1, "Scanner not ready: optical error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x40:
 | |
|                             DBG(1, "Scanner not ready: optical error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x50:
 | |
|                             DBG(1, "Scanner not ready: marker error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x60:
 | |
|                             DBG(1, "Scanner not ready: mechanical error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x70:
 | |
|                             DBG(1, "Scanner not ready: hardware error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x80:
 | |
|                             DBG(1, "Scanner not ready: hardware error\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                           case 0x90:
 | |
|                           default:
 | |
|                             DBG(5, "Scanner not ready: undocumented reason\n");
 | |
|                             return SANE_STATUS_IO_ERROR;
 | |
|                         }
 | |
|                   }
 | |
|               case 0x03: /* medium error */
 | |
| 		DBG(5, "medium error: undocumented reason\n");
 | |
| 		return SANE_STATUS_IO_ERROR;
 | |
|               case 0x04: /* hardware error */
 | |
| 		DBG(1, "general hardware error\n");
 | |
| 		return SANE_STATUS_IO_ERROR;
 | |
|               case 0x05: /* illegal request */
 | |
|                 DBG(10, "error: illegal request\n");
 | |
|                 return SANE_STATUS_IO_ERROR;
 | |
|               case 0x06: /* unit attention */
 | |
| 		DBG(5, "unit attention: exact reason not documented\n");
 | |
| 		return SANE_STATUS_IO_ERROR;
 | |
|               case 0x0B: /* data remains */
 | |
|                 DBG(5, "error: aborted command\n");
 | |
|                 return SANE_STATUS_IO_ERROR;
 | |
|               default:
 | |
|                 DBG(5, "error: sense code not documented\n");
 | |
|                 return SANE_STATUS_IO_ERROR;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|   return SANE_STATUS_IO_ERROR;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| test_unit_ready (int fd)
 | |
| {
 | |
|   static u_char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< test_unit_ready ");
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static SANE_Status
 | |
| request_sense (int fd, void *sense_buf, size_t *sense_size)
 | |
| {
 | |
|   static u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, SENSE_LEN, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< request_sense ");
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_buf, sense_size);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static SANE_Status
 | |
| inquiry (int fd, void *inq_buf, size_t *inq_size)
 | |
| {
 | |
|   static u_char cmd[] = {INQUIRY, 0, 0, 0, INQUIRY_LEN, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< inquiry ");
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), inq_buf, inq_size);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| mode_select_mud (int fd, int mud)
 | |
| {
 | |
|   static u_char cmd[6 + MODEPARAM_LEN] =
 | |
|   {MODE_SELECT6, 0x10, 0, 0, MODEPARAM_LEN, 0};
 | |
|   mode_select_param *mp;
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< mode_select_mud ");
 | |
| 
 | |
|   mp = (mode_select_param *)(cmd + 6);
 | |
|   memset (mp, 0, MODEPARAM_LEN);
 | |
|   mp->mode_param_header1 = 11;
 | |
|   mp->page_code = 3;
 | |
|   mp->page_length = 6;
 | |
|   mp->mud[0] = mud >> 8;
 | |
|   mp->mud[1] = mud & 0xFF;
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static SANE_Status
 | |
| mode_select_adf_fsu (int fd, int mode)
 | |
| {
 | |
|   static u_char cmd[6 + MODE_SUBDEV_LEN] =
 | |
|                         {MODE_SELECT6, 0x10, 0, 0, MODE_SUBDEV_LEN, 0};
 | |
|   mode_select_subdevice *mp;
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< mode_select_adf_fsu ");
 | |
| 
 | |
|   mp = (mode_select_subdevice *)(cmd + 6);
 | |
|   memset (mp, 0, MODE_SUBDEV_LEN);
 | |
|   mp->page_code = 0x20;
 | |
|   mp->page_length = 26;
 | |
|   switch (mode)
 | |
|     {
 | |
|       case SCAN_SIMPLE:
 | |
|         mp->a_mode = 0x40;
 | |
|         mp->f_mode = 0x40;
 | |
|         break;
 | |
|       case SCAN_WITH_FSU:
 | |
|         mp->a_mode = 0;
 | |
|         mp->f_mode = 0x40;
 | |
|         break;
 | |
|       case SCAN_WITH_ADF:
 | |
|         mp->a_mode = 0x40;
 | |
|         mp->f_mode = 0;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static SANE_Status wait_ready(int fd);
 | |
| 
 | |
| static SANE_Status
 | |
| mode_sense (int fd, void *modeparam_buf, size_t * modeparam_size,
 | |
|             int page)
 | |
| {
 | |
|   static u_char cmd[6] = {MODE_SENSE6, 0, 0, 0, 0, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< mode_sense ");
 | |
|   cmd[0] = 0x1a;
 | |
|   cmd[2] = page;
 | |
|   cmd[4] = *modeparam_size;
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), modeparam_buf,
 | |
| 			   modeparam_size);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| scan (int fd)
 | |
| {
 | |
|   static u_char cmd[] = {SCAN, 0, 0, 0, 0, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< scan ");
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static SANE_Status
 | |
| send_diagnostics (int fd)
 | |
| {
 | |
|   static u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< send_diagnostics ");
 | |
| 
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static SANE_Status
 | |
| set_window (int fd, window_param *wp, int len)
 | |
| {
 | |
|   static u_char cmd[10 + WINDOW_LEN] =
 | |
|                         {SET_WINDOW, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | |
|   window_param *winp;
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< set_window ");
 | |
| 
 | |
|   cmd[8] = len;
 | |
|   winp = (window_param *)(cmd + 10);
 | |
|   memset (winp, 0, WINDOW_LEN);
 | |
|   memcpy (winp, wp, len);
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| 
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| get_window (int fd, void *buf, size_t * buf_size)
 | |
| {
 | |
|   static u_char cmd[10] = {GET_WINDOW, 0, 0, 0, 0, 0, 0, 0, WINDOW_LEN, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< get_window ");
 | |
| 
 | |
|   cmd[8] = *buf_size;
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static SANE_Status
 | |
| get_data_buffer_status (int fd, void *buf, size_t *buf_size)
 | |
| {
 | |
|   static u_char cmd[10] =
 | |
|                         {GET_DATA_BUFFER_STATUS, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | |
|   SANE_Status status;
 | |
|   DBG (11, "<< get_data_buffer_status ");
 | |
| 
 | |
|   cmd[8] = *buf_size;
 | |
|   status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size);
 | |
| 
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_FORK
 | |
| 
 | |
| /* the following four functions serve simply the purpose
 | |
|    to avoid "over-optimised" code when reader_process and
 | |
|    read_data wait for the buffer to become ready. The simple
 | |
|    while-loops in these functions which check the buffer
 | |
|    status may be optimised so that the machine code only
 | |
|    operates with registers instead of using the variable
 | |
|    values stored in memory. (This is only a workaround -
 | |
|    it would be better to set a compiler pragma, which ensures
 | |
|    that the program looks into the RAM in these while loops --
 | |
|    but unfortunately I could not find appropriate information
 | |
|    about this at least for gcc, not to speak about other
 | |
|    compilers...
 | |
|    Abel)
 | |
| */
 | |
| 
 | |
| static int
 | |
| cancel_requested(NEC_Scanner *s)
 | |
| {
 | |
|   return s->rdr_ctl->cancel;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| rdr_status(NEC_Scanner *s)
 | |
| {
 | |
|   return s->rdr_ctl->status;
 | |
| }
 | |
| 
 | |
| static int
 | |
| buf_status(NEC_shmem_ctl *s)
 | |
| {
 | |
|   return s->shm_status;
 | |
| }
 | |
| 
 | |
| static int
 | |
| reader_running(NEC_Scanner *s)
 | |
| {
 | |
|   return s->rdr_ctl->running;
 | |
| }
 | |
| 
 | |
| static int
 | |
| reader_process(NEC_Scanner *s)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   sigset_t sigterm_set;
 | |
|   static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | |
|   int full_count = 0, counted;
 | |
|   size_t waitindex, cmdindex;
 | |
|   size_t bytes_to_queue;
 | |
|   size_t nread;
 | |
|   size_t max_bytes_per_read;
 | |
|   int max_queue;
 | |
|   int i;
 | |
|   NEC_shmem_ctl *bc;
 | |
| 
 | |
|   s->rdr_ctl->running = 1;
 | |
|   DBG(11, "<< reader_process\n");
 | |
| 
 | |
|   sigemptyset (&sigterm_set);
 | |
| 
 | |
|   bytes_to_queue = s->bytes_to_read;
 | |
| 
 | |
|   max_bytes_per_read = s->dev->info.bufsize / s->params.bytes_per_line;
 | |
|   if (max_bytes_per_read)
 | |
|     max_bytes_per_read *= s->params.bytes_per_line;
 | |
|   else
 | |
|     /* this is a really tiny buffer..*/
 | |
|     max_bytes_per_read = s->dev->info.bufsize;
 | |
| 
 | |
|   /*  wait_ready(s->fd); */
 | |
| 
 | |
|   if (s->dev->info.queued_reads <= s->dev->info.buffers)
 | |
|     max_queue = s->dev->info.queued_reads;
 | |
|   else
 | |
|     max_queue = s->dev->info.buffers;
 | |
|   for (i = 0; i < max_queue; i++)
 | |
|     {
 | |
|       bc = &s->rdr_ctl->buf_ctl[i];
 | |
|       if (bytes_to_queue)
 | |
|         {
 | |
|           nread = bytes_to_queue;
 | |
|           if (nread > max_bytes_per_read)
 | |
|             nread = max_bytes_per_read;
 | |
|           bc->used = nread;
 | |
|           cmd[6] = nread >> 16;
 | |
|           cmd[7] = nread >> 8;
 | |
|           cmd[8] = nread;
 | |
| #ifdef QUEUEDEBUG
 | |
|           DBG(2, "reader: req_enter...\n");
 | |
| #endif
 | |
|           status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
 | |
|                      bc->buffer,
 | |
|                     &bc->used,
 | |
|                     &bc->qid);
 | |
| #ifdef QUEUEDEBUG
 | |
|           DBG(2, "reader: req_enter ok\n");
 | |
| #endif
 | |
|           if (status != SANE_STATUS_GOOD)
 | |
|             {
 | |
|               DBG(1, "reader_process: read command failed: %s",
 | |
|                   sane_strstatus(status));
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|               sanei_scsi_req_flush_all_extended(s->fd);
 | |
| #else
 | |
|                sanei_scsi_req_flush_all();
 | |
| #endif
 | |
|               s->rdr_ctl->status = status;
 | |
|               s->rdr_ctl->running = 0;
 | |
|               return 2;
 | |
|             }
 | |
|           bc->shm_status = SHM_BUSY;
 | |
|           bc->nreq = bc->used;
 | |
|           bytes_to_queue -= bc->nreq;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           bc->used = 0;
 | |
|           bc->shm_status = SHM_EMPTY;
 | |
|         }
 | |
|     }
 | |
|   waitindex = 0;
 | |
|   cmdindex = i % s->dev->info.buffers;
 | |
| 
 | |
|   while(s->bytes_to_read > 0)
 | |
|     {
 | |
|       if (cancel_requested(s))
 | |
|         {
 | |
| #ifdef QUEUEDEBUG
 | |
|           DBG(2, "reader: flushing requests...\n");
 | |
| #endif
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|           sanei_scsi_req_flush_all_extended(s->fd);
 | |
| #else
 | |
|           sanei_scsi_req_flush_all();
 | |
| #endif
 | |
| #ifdef QUEUEDEBUG
 | |
|           DBG(2, "reader: flushing requests ok\n");
 | |
| #endif
 | |
|           s->rdr_ctl->cancel = 0;
 | |
|           s->rdr_ctl->status = SANE_STATUS_CANCELLED;
 | |
|           s->rdr_ctl->running = 0;
 | |
|           DBG(11, " reader_process (cancelled) >>\n");
 | |
|           return 1;
 | |
|         }
 | |
| 
 | |
|       bc = &s->rdr_ctl->buf_ctl[waitindex];
 | |
|       if (bc->shm_status == SHM_BUSY)
 | |
|         {
 | |
| #ifdef DEBUG
 | |
|           {
 | |
|             struct timeval t;
 | |
|             gettimeofday(&t, 0);
 | |
|             DBG(2, "rd: waiting for data %li.%06li\n", t.tv_sec, t.tv_usec);
 | |
|           }
 | |
| #endif
 | |
| #ifdef QUEUEDEBUG
 | |
|           DBG(2, "reader: req_wait...\n");
 | |
| #endif
 | |
|           status = sanei_scsi_req_wait(bc->qid);
 | |
| #ifdef QUEUEDEBUG
 | |
|           DBG(2, "reader: req_wait ok\n");
 | |
| #endif
 | |
| #ifdef DEBUG
 | |
|           {
 | |
|             struct timeval t;
 | |
|             gettimeofday(&t, 0);
 | |
|             DBG(2, "rd: data received    %li.%06li\n", t.tv_sec, t.tv_usec);
 | |
|           }
 | |
| #endif
 | |
|           if (status != SANE_STATUS_GOOD)
 | |
|             {
 | |
|               DBG(1, "reader_process: read command failed: %s",
 | |
|                   sane_strstatus(status));
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|               sanei_scsi_req_flush_all_extended(s->fd);
 | |
| #else
 | |
|               sanei_scsi_req_flush_all();
 | |
| #endif
 | |
|               s->rdr_ctl->status = status;
 | |
|               s->rdr_ctl->running = 0;
 | |
|               return 2;
 | |
|             }
 | |
|           s->bytes_to_read -= bc->used;
 | |
|           bytes_to_queue += bc->nreq - bc->used;
 | |
|           bc->start = 0;
 | |
|           bc->shm_status = SHM_FULL;
 | |
| 
 | |
|           waitindex++;
 | |
|           if (waitindex == s->dev->info.buffers)
 | |
|             waitindex = 0;
 | |
| 
 | |
|         }
 | |
| 
 | |
|       if (bytes_to_queue)
 | |
|         {
 | |
|           /* wait until the next buffer is completely read via read_data */
 | |
|           bc = &s->rdr_ctl->buf_ctl[cmdindex];
 | |
|           counted = 0;
 | |
|           while (buf_status(bc) != SHM_EMPTY)
 | |
|             {
 | |
|               if (!counted)
 | |
|                 {
 | |
|                   counted = 1;
 | |
|                   full_count++;
 | |
|                 }
 | |
|               if (cancel_requested(s))
 | |
|                 {
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|                   sanei_scsi_req_flush_all_extended(s->fd);
 | |
| #else
 | |
|                   sanei_scsi_req_flush_all();
 | |
| #endif
 | |
|                   s->rdr_ctl->cancel = 0;
 | |
|                   s->rdr_ctl->status = SANE_STATUS_CANCELLED;
 | |
|                   s->rdr_ctl->running = 0;
 | |
|                   DBG(11, " reader_process (cancelled) >>\n");
 | |
|                   return 1;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|           nread = bytes_to_queue;
 | |
|           if (nread > max_bytes_per_read)
 | |
|             nread = max_bytes_per_read;
 | |
|           bc->used = nread;
 | |
|           cmd[6] = nread >> 16;
 | |
|           cmd[7] = nread >> 8;
 | |
|           cmd[8] = nread;
 | |
|           status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd),
 | |
|                     bc->buffer, &bc->used, &bc->qid);
 | |
|           if (status != SANE_STATUS_GOOD)
 | |
|             {
 | |
|               DBG(1, "reader_process: read command failed: %s",
 | |
|                   sane_strstatus(status));
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|               sanei_scsi_req_flush_all_extended(s->fd);
 | |
| #else
 | |
|               sanei_scsi_req_flush_all();
 | |
| #endif
 | |
|               s->rdr_ctl->status = status;
 | |
|               s->rdr_ctl->running = 0;
 | |
|               return 2;
 | |
|             }
 | |
|           bc->shm_status = SHM_BUSY;
 | |
|           bc->nreq = nread;
 | |
|           bytes_to_queue -= nread;
 | |
| 
 | |
|           cmdindex++;
 | |
|           if (cmdindex == s->dev->info.buffers)
 | |
|             cmdindex = 0;
 | |
|         }
 | |
| 
 | |
|       if (cancel_requested(s))
 | |
|         {
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|           sanei_scsi_req_flush_all_extended(s->fd);
 | |
| #else
 | |
|           sanei_scsi_req_flush_all();
 | |
| #endif
 | |
|           s->rdr_ctl->cancel = 0;
 | |
|           s->rdr_ctl->status = SANE_STATUS_CANCELLED;
 | |
|           s->rdr_ctl->running = 0;
 | |
|           DBG(11, " reader_process (cancelled) >>\n");
 | |
|           return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   DBG(1, "buffer full conditions: %i\n", full_count);
 | |
|   DBG(11, " reader_process>>\n");
 | |
| 
 | |
|   s->rdr_ctl->running = 0;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size)
 | |
| {
 | |
|   size_t copysize, copied = 0;
 | |
|   NEC_shmem_ctl *bc;
 | |
| 
 | |
|   DBG(11, "<< read_data ");
 | |
| 
 | |
|   bc = &s->rdr_ctl->buf_ctl[s->read_buff];
 | |
| 
 | |
|   while (copied < *buf_size)
 | |
|     {
 | |
|       /* wait until the reader process delivers data or a scanner error occurs: */
 | |
|       while (   buf_status(bc) != SHM_FULL
 | |
|              && rdr_status(s) == SANE_STATUS_GOOD)
 | |
|         {
 | |
|           usleep(10); /* could perhaps be longer. make this user configurable?? */
 | |
|         }
 | |
| 
 | |
|       if (rdr_status(s) != SANE_STATUS_GOOD)
 | |
|         {
 | |
|           return rdr_status(s);
 | |
|           DBG(11, ">>\n");
 | |
|         }
 | |
| 
 | |
|       copysize = bc->used - bc->start;
 | |
| 
 | |
|       if (copysize > *buf_size - copied )
 | |
|         copysize = *buf_size - copied;
 | |
| 
 | |
|       memcpy(buf, &(bc->buffer[bc->start]), copysize);
 | |
| 
 | |
|       copied += copysize;
 | |
|       buf = &buf[copysize];
 | |
| 
 | |
|       bc->start += copysize;
 | |
|       if (bc->start >= bc->used)
 | |
|         {
 | |
|           bc->start = 0;
 | |
|           bc->shm_status = SHM_EMPTY;
 | |
|           s->read_buff++;
 | |
|           if (s->read_buff == s->dev->info.buffers)
 | |
|             s->read_buff = 0;
 | |
|           bc = &s->rdr_ctl->buf_ctl[s->read_buff];
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   DBG(11, ">>\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| #else /* don't USE_FORK: */
 | |
| 
 | |
| static SANE_Status
 | |
| read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size)
 | |
| {
 | |
|   static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   size_t remain = *buf_size;
 | |
|   size_t nread;
 | |
|   DBG (11, "<< read_data ");
 | |
| 
 | |
|   /* sane_read_shuffled requires that read_data returns
 | |
|      exactly *buf_size bytes, so it must be guaranteed here.
 | |
|      Further make sure that not more bytes are read in than
 | |
|      sanei_scsi_max_request_size allows, to avoid a failure
 | |
|      of the read command
 | |
|   */
 | |
|   while (remain > 0)
 | |
|     {
 | |
|       nread = remain;
 | |
|       if (nread > s->dev->info.bufsize)
 | |
|         nread = s->dev->info.bufsize;
 | |
|       cmd[6] = nread >> 16;
 | |
|       cmd[7] = nread >> 8;
 | |
|       cmd[8] = nread;
 | |
|       status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd),
 | |
|                  &buf[*buf_size - remain], &nread);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
|         {
 | |
|           DBG(11, ">>\n");
 | |
|           return(status);
 | |
|         }
 | |
|       remain -= nread;
 | |
|     }
 | |
|   DBG (11, ">>\n");
 | |
|   return (status);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static size_t
 | |
| max_string_size (const SANE_String_Const strings[])
 | |
| {
 | |
|   size_t size, max_size = 0;
 | |
|   int i;
 | |
|   DBG (10, "<< max_string_size ");
 | |
| 
 | |
|   for (i = 0; strings[i]; ++i)
 | |
|     {
 | |
|       size = strlen (strings[i]) + 1;
 | |
|       if (size > max_size)
 | |
| 	max_size = size;
 | |
|     }
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return max_size;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| wait_ready(int fd)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   int retry = 0;
 | |
| 
 | |
|   while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD)
 | |
|   {
 | |
|     DBG (5, "wait_ready failed (%d)\n", retry);
 | |
|     DBG (5, "wait_ready status = (%d)\n", status);
 | |
|     if (retry++ > 15){
 | |
| 	return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|     sleep(3);
 | |
|   }
 | |
|   return (status);
 | |
| 
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| attach (const char *devnam, NEC_Device ** devp)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   NEC_Device *dev;
 | |
|   NEC_Sense_Data sensedat;
 | |
| 
 | |
|   int fd;
 | |
|   unsigned char inquiry_data[INQUIRY_LEN];
 | |
|   const unsigned char *model_name;
 | |
|   mode_sense_param msp;
 | |
|   size_t buf_size;
 | |
|   DBG (10, "<< attach ");
 | |
| 
 | |
|   for (dev = first_dev; dev; dev = dev->next)
 | |
|     {
 | |
|       if (strcmp (dev->sane.name, devnam) == 0)
 | |
| 	{
 | |
| 	  if (devp)
 | |
| 	    *devp = dev;
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   sensedat.model = unknown;
 | |
|   sensedat.complain_on_adf_error = 0;
 | |
|   DBG (3, "attach: opening %s\n", devnam);
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|   {
 | |
|     int bufsize = 4096;
 | |
|     status = sanei_scsi_open_extended (devnam, &fd, &sense_handler, &sensedat, &bufsize);
 | |
|     if (status != SANE_STATUS_GOOD)
 | |
|       {
 | |
|         DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
 | |
|         return (status);
 | |
|       }
 | |
|     if (bufsize < 4096)
 | |
|       {
 | |
|         DBG(1, "attach: open failed. no memory\n");
 | |
|         sanei_scsi_close(fd);
 | |
|         return SANE_STATUS_NO_MEM;
 | |
|       }
 | |
|   }
 | |
| #else
 | |
|   status = sanei_scsi_open (devnam, &fd, &sense_handler, &sensedat);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
 | |
|       return (status);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   DBG (3, "attach: sending INQUIRY\n");
 | |
|   memset (inquiry_data, 0, sizeof (inquiry_data));
 | |
|   buf_size = sizeof (inquiry_data);
 | |
|   status = inquiry (fd, inquiry_data, &buf_size);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (fd);
 | |
|       return (status);
 | |
|     }
 | |
| 
 | |
|   if (inquiry_data[0] == 6 && strncmp ((char *)inquiry_data + 8, "NEC", 3) == 0)
 | |
|     {
 | |
|       if (strncmp ((char *)inquiry_data + 16, "PC-IN500/4C", 11) == 0)
 | |
|         sensedat.model = PCIN500;
 | |
|       else
 | |
|         sensedat.model = unknown;
 | |
|     }
 | |
| 
 | |
|   if (sensedat.model == unknown)
 | |
|     {
 | |
|       DBG (1, "attach: device doesn't look like a NEC scanner\n");
 | |
|       DBG (1, "      : Only PC-IN500/4C is supported.\n");
 | |
|       sanei_scsi_close (fd);
 | |
|       return (SANE_STATUS_INVAL);
 | |
|     }
 | |
| 
 | |
|   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 MODE SELECT\n");
 | |
| 
 | |
|   if (sensedat.model == PCIN500)
 | |
|     status = mode_select_mud (fd, DEFAULT_MUD_1200);
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "attach: MODE_SELECT_MUD failed\n");
 | |
|       sanei_scsi_close (fd);
 | |
|       return (SANE_STATUS_INVAL);
 | |
|     }
 | |
| 
 | |
|   DBG (3, "attach: sending MODE SENSE/MUP page\n");
 | |
|   memset (&msp, 0, sizeof (msp));
 | |
|   buf_size = sizeof (msp);
 | |
|   status = mode_sense (fd, &msp, &buf_size, 3);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "attach: MODE_SENSE/MUP page failed\n");
 | |
|       sanei_scsi_close (fd);
 | |
|       return (SANE_STATUS_INVAL);
 | |
|     }
 | |
| #ifdef DEBUG_NEC
 | |
|   DBG (3,"attach: MODE SENSE parameter\n");
 | |
|       DBG(11, "%02x %02x %02x %02x "
 | |
|               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
 | |
|               msp.mode_data_length,
 | |
| 	      msp.mode_param_header2,
 | |
| 	      msp.mode_param_header3,
 | |
| 	      msp.mode_desciptor_length,
 | |
| 	      msp.page_code,
 | |
| 	      msp.page_length,
 | |
| 	      msp.bmu,
 | |
| 	      msp.res2,
 | |
| 	      msp.mud[0],
 | |
| 	      msp.mud[1],
 | |
| 	      msp.res3,
 | |
| 	      msp.res4);
 | |
| #endif
 | |
|   dev = malloc (sizeof (*dev));
 | |
|   if (!dev)
 | |
|     return (SANE_STATUS_NO_MEM);
 | |
|   memset (dev, 0, sizeof (*dev));
 | |
| 
 | |
|   dev->sane.name = (SANE_String) strdup (devnam);
 | |
|   dev->sane.vendor = "NEC";
 | |
|   model_name = inquiry_data + 16;
 | |
|   dev->sane.model  = strndup ((const char *)model_name, 10);
 | |
|   dev->sane.type = "flatbed scanner";
 | |
| 
 | |
|   dev->sensedat.model = sensedat.model;
 | |
| 
 | |
|   DBG (5, "dev->sane.name = %s\n", dev->sane.name);
 | |
|   DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor);
 | |
|   DBG (5, "dev->sane.model = %s\n", dev->sane.model);
 | |
|   DBG (5, "dev->sane.type = %s\n", dev->sane.type);
 | |
| 
 | |
|   if (sensedat.model == PCIN500)
 | |
|     dev->info.res_range.quant = 10;
 | |
|   else
 | |
|     dev->info.res_range.quant = 0;
 | |
| 
 | |
|   dev->info.tl_x_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
 | |
|   dev->info.br_x_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
 | |
|   dev->info.tl_y_ranges[SCAN_SIMPLE].min = SANE_FIX(0);
 | |
|   dev->info.br_y_ranges[SCAN_SIMPLE].min = SANE_FIX(1);
 | |
|   dev->info.tl_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
 | |
|   dev->info.br_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
 | |
|   dev->info.tl_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
 | |
|   dev->info.br_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0);
 | |
| 
 | |
|   if (sensedat.model == PCIN500)
 | |
|     dev->info.res_default = 15;
 | |
|   else
 | |
|     dev->info.res_default = 150;
 | |
|   dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(209);
 | |
|   dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210);
 | |
|   dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(296);
 | |
|   dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297);
 | |
| 
 | |
|   dev->info.bmu = msp.bmu;
 | |
|   dev->info.mud = (msp.mud[0] << 8) + msp.mud[1];
 | |
| 
 | |
|   dev->info.adf_fsu_installed = 0;
 | |
|   if (dev->sensedat.model == PCIN500)
 | |
|     {
 | |
|       dev->info.res_range.max = 48;
 | |
|       dev->info.res_range.min = 5;
 | |
| 
 | |
|       dev->info.x_default = SANE_FIX(210);
 | |
|       dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
 | |
|       dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
 | |
| 
 | |
|       dev->info.y_default = SANE_FIX(297);
 | |
|       dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
 | |
|       dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       dev->info.res_range.max = 400;
 | |
|       dev->info.res_range.min = 50;
 | |
| 
 | |
|       dev->info.x_default = SANE_FIX(210);
 | |
|       dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
 | |
|       dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */
 | |
| 
 | |
|       dev->info.y_default = SANE_FIX(297);
 | |
|       dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
 | |
|       dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */
 | |
|     }
 | |
|   sanei_scsi_close (fd);
 | |
| 
 | |
|   dev->info.threshold_range.min = 1;
 | |
|   dev->info.threshold_range.max = 255;
 | |
|   dev->info.threshold_range.quant = 0;
 | |
| 
 | |
|   dev->info.tint_range.min = 1;
 | |
|   dev->info.tint_range.max = 255;
 | |
|   dev->info.tint_range.quant = 0;
 | |
| 
 | |
|   dev->info.color_range.min = 1;
 | |
|   dev->info.color_range.max = 255;
 | |
|   dev->info.color_range.quant = 0;
 | |
| 
 | |
|   DBG (5, "res_default=%d\n", dev->info.res_default);
 | |
|   DBG (5, "res_range.max=%d\n", dev->info.res_range.max);
 | |
|   DBG (5, "res_range.min=%d\n", dev->info.res_range.min);
 | |
|   DBG (5, "res_range.quant=%d\n", dev->info.res_range.quant);
 | |
| 
 | |
|   DBG (5, "x_default=%f\n", SANE_UNFIX(dev->info.x_default));
 | |
|   DBG (5, "tl_x_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].max));
 | |
|   DBG (5, "tl_x_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].min));
 | |
|   DBG (5, "tl_x_range[0].quant=%d\n", dev->info.tl_x_ranges[SCAN_SIMPLE].quant);
 | |
|   DBG (5, "br_x_range[0].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].max));
 | |
|   DBG (5, "br_x_range[0].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].min));
 | |
|   DBG (5, "br_x_range[0].quant=%d\n", dev->info.br_x_ranges[SCAN_SIMPLE].quant);
 | |
|   DBG (5, "y_default=%f\n", SANE_UNFIX(dev->info.y_default));
 | |
|   DBG (5, "tl_y_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].max));
 | |
|   DBG (5, "tl_y_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].min));
 | |
|   DBG (5, "tl_y_range[0].quant=%d\n", dev->info.tl_y_ranges[SCAN_SIMPLE].quant);
 | |
|   DBG (5, "br_y_range[0].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].max));
 | |
|   DBG (5, "br_y_range[0].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].min));
 | |
|   DBG (5, "br_y_range[0].quant=%d\n", dev->info.br_y_ranges[SCAN_SIMPLE].quant);
 | |
| 
 | |
|   if (dev->info.adf_fsu_installed & HAVE_FSU)
 | |
|     {
 | |
|       DBG (5, "tl_x_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].max));
 | |
|       DBG (5, "tl_x_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].min));
 | |
|       DBG (5, "tl_x_range[1].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_FSU].quant);
 | |
|       DBG (5, "br_x_range[1].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].max));
 | |
|       DBG (5, "br_x_range[1].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].min));
 | |
|       DBG (5, "br_x_range[1].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_FSU].quant);
 | |
|       DBG (5, "tl_y_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].max));
 | |
|       DBG (5, "tl_y_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].min));
 | |
|       DBG (5, "tl_y_range[1].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_FSU].quant);
 | |
|       DBG (5, "br_y_range[1].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].max));
 | |
|       DBG (5, "br_y_range[1].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].min));
 | |
|       DBG (5, "br_y_range[1].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_FSU].quant);
 | |
|     }
 | |
| 
 | |
|   if (dev->info.adf_fsu_installed & HAVE_ADF)
 | |
|     {
 | |
|       DBG (5, "tl_x_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].max));
 | |
|       DBG (5, "tl_x_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].min));
 | |
|       DBG (5, "tl_x_range[2].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_ADF].quant);
 | |
|       DBG (5, "br_x_range[2].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].max));
 | |
|       DBG (5, "br_x_range[2].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].min));
 | |
|       DBG (5, "br_x_range[2].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_ADF].quant);
 | |
|       DBG (5, "tl_y_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].max));
 | |
|       DBG (5, "tl_y_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].min));
 | |
|       DBG (5, "tl_y_range[2].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_ADF].quant);
 | |
|       DBG (5, "br_y_range[2].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].max));
 | |
|       DBG (5, "br_y_range[2].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].min));
 | |
|       DBG (5, "br_y_range[2].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_ADF].quant);
 | |
|     }
 | |
| 
 | |
|   DBG (5, "bmu=%d\n", dev->info.bmu);
 | |
|   DBG (5, "mud=%d\n", dev->info.mud);
 | |
| 
 | |
|   ++num_devices;
 | |
|   dev->next = first_dev;
 | |
|   first_dev = dev;
 | |
| 
 | |
|   if (devp)
 | |
|     *devp = dev;
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_GOOD);
 | |
| }
 | |
| 
 | |
| /* Enabling / disabling of gamma options.
 | |
|    Depends on many user settable options, so lets put it into
 | |
|    one function to be called by init_options and by sane_control_option
 | |
| 
 | |
| */
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| static void
 | |
| set_gamma_caps(NEC_Scanner *s)
 | |
| {
 | |
|   /* neither fixed nor custom gamma for line art modes */
 | |
|   if (   strcmp(s->val[OPT_MODE].s, M_LINEART) == 0
 | |
|       || strcmp(s->val[OPT_MODE].s, M_LINEART_COLOR) == 0)
 | |
|     {
 | |
|       s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
|   else if (strcmp(s->val[OPT_MODE].s, M_GRAY) == 0)
 | |
|     {
 | |
|       s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
 | |
|       if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
 | |
|         {
 | |
|           s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | |
|         }
 | |
|       s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* color mode */
 | |
|       s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
 | |
|       if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
 | |
|         {
 | |
|           s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
 | |
|           s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
 | |
|         }
 | |
|       s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
| }
 | |
| #endif /* USE_CUSTOM_GAMMA */
 | |
| 
 | |
| /* The next function is a slightly modified version of sanei_constrain_value
 | |
|    Instead of returning status information like STATUS_INVAL, it adjusts
 | |
|    an invaild value to the nearest allowed one.
 | |
| */
 | |
| static void
 | |
| clip_value (const SANE_Option_Descriptor * opt, void * value)
 | |
| {
 | |
|   const SANE_String_Const * string_list;
 | |
|   const SANE_Word * word_list;
 | |
|   int i, num_matches, match;
 | |
|   const SANE_Range * range;
 | |
|   SANE_Word w, v;
 | |
|   size_t len;
 | |
| 
 | |
|   switch (opt->constraint_type)
 | |
|     {
 | |
|     case SANE_CONSTRAINT_RANGE:
 | |
|       w = *(SANE_Word *) value;
 | |
|       range = opt->constraint.range;
 | |
| 
 | |
|       if (w < range->min)
 | |
|         w = range->min;
 | |
|       else if (w > range->max)
 | |
| 	w = range->max;
 | |
| 
 | |
|       if (range->quant)
 | |
| 	{
 | |
| 	  v = (w - range->min + range->quant/2) / range->quant;
 | |
| 	  w = v * range->quant + range->min;
 | |
| 	  *(SANE_Word*) value = w;
 | |
| 	}
 | |
|       break;
 | |
| 
 | |
|     case SANE_CONSTRAINT_WORD_LIST:
 | |
|       w = *(SANE_Word *) value;
 | |
|       word_list = opt->constraint.word_list;
 | |
|       for (i = 1; w != word_list[i]; ++i)
 | |
| 	if (i >= word_list[0])
 | |
| 	  /* somewhat arbitrary... Would be better to have a default value
 | |
| 	     explicitly defined.
 | |
| 	  */
 | |
| 	  *(SANE_Word*) value = word_list[1];
 | |
|       break;
 | |
| 
 | |
|     case SANE_CONSTRAINT_STRING_LIST:
 | |
|       /* Matching algorithm: take the longest unique match ignoring
 | |
| 	 case.  If there is an exact match, it is admissible even if
 | |
| 	 the same string is a prefix of a longer option name. */
 | |
|       string_list = opt->constraint.string_list;
 | |
|       len = strlen (value);
 | |
| 
 | |
|       /* count how many matches of length LEN characters we have: */
 | |
|       num_matches = 0;
 | |
|       match = -1;
 | |
|       for (i = 0; string_list[i]; ++i)
 | |
| 	if (strncasecmp (value, string_list[i], len) == 0
 | |
| 	    && len <= strlen (string_list[i]))
 | |
| 	  {
 | |
| 	    match = i;
 | |
| 	    if (len == strlen (string_list[i]))
 | |
| 	      {
 | |
| 		/* exact match... */
 | |
| 		if (strcmp (value, string_list[i]) != 0)
 | |
| 		  /* ...but case differs */
 | |
| 		  strcpy (value, string_list[match]);
 | |
| 	      }
 | |
| 	    ++num_matches;
 | |
| 	  }
 | |
| 
 | |
|       if (num_matches > 1)
 | |
|         /* xxx quite arbitrary... We could also choose the first match
 | |
|         */
 | |
|         strcpy(value, string_list[match]);
 | |
|       else if (num_matches == 1)
 | |
|         strcpy (value, string_list[match]);
 | |
|       else
 | |
|         strcpy (value, string_list[0]);
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* make sure that enough memory is allocated for each string,
 | |
|    so that the strcpy in sane_control_option / set value cannot
 | |
|    write behind the end of the allocated memory.
 | |
| */
 | |
| static SANE_Status
 | |
| init_string_option(NEC_Scanner *s, SANE_String_Const name,
 | |
|    SANE_String_Const title, SANE_String_Const desc,
 | |
|    const SANE_String_Const *string_list, int option, int default_index)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   s->opt[option].name = name;
 | |
|   s->opt[option].title = title;
 | |
|   s->opt[option].desc = desc;
 | |
|   s->opt[option].type = SANE_TYPE_STRING;
 | |
|   s->opt[option].size = max_string_size (string_list);
 | |
|   s->opt[option].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[option].constraint.string_list = string_list;
 | |
|   s->val[option].s = malloc(s->opt[option].size);
 | |
|   if (s->val[option].s == 0)
 | |
|     {
 | |
|       for (i = 1; i < NUM_OPTIONS; i++)
 | |
|         {
 | |
|           if (s->val[i].s && s->opt[i].type == SANE_TYPE_STRING)
 | |
|             free(s->val[i].s);
 | |
|         }
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
|   strcpy(s->val[option].s, string_list[default_index]);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| init_options (NEC_Scanner * s)
 | |
| {
 | |
|   int i, default_source;
 | |
|   SANE_Word scalar;
 | |
|   DBG (10, "<< init_options ");
 | |
| 
 | |
|   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->val[i].s = 0;
 | |
|     }
 | |
| 
 | |
|   s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | |
|   s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | |
|   s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | |
|   s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | |
| 
 | |
|   /* Mode group: */
 | |
|   s->opt[OPT_MODE_GROUP].title = "Scan Mode";
 | |
|   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;
 | |
| 
 | |
|   /* scan mode */
 | |
|   init_string_option(s, SANE_NAME_SCAN_MODE, SANE_TITLE_SCAN_MODE,
 | |
|     SANE_DESC_SCAN_MODE, mode_list, OPT_MODE, MODES_COLOR);
 | |
| 
 | |
|   /* half tone */
 | |
|   init_string_option(s, SANE_NAME_HALFTONE_PATTERN, SANE_TITLE_HALFTONE_PATTERN,
 | |
|     SANE_DESC_HALFTONE " (not support)", halftone_list, OPT_HALFTONE, 0);
 | |
| 
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   i = 0;
 | |
|   default_source = -1;
 | |
| 
 | |
|   if (s->dev->info.adf_fsu_installed & HAVE_ADF)
 | |
|     {
 | |
|       s->dev->info.scansources[i++] = use_adf;
 | |
|       default_source = SCAN_WITH_ADF;
 | |
|     }
 | |
|   if (s->dev->info.adf_fsu_installed & HAVE_FSU)
 | |
|     {
 | |
|       s->dev->info.scansources[i++] = use_fsu;
 | |
|       if (default_source < 0)
 | |
|         default_source = SCAN_WITH_FSU;
 | |
|     }
 | |
|   s->dev->info.scansources[i++] = use_simple;
 | |
|     if (default_source < 0)
 | |
|       default_source = SCAN_SIMPLE;
 | |
|   s->dev->info.scansources[i] = 0;
 | |
| 
 | |
|   init_string_option(s, SANE_NAME_SCAN_SOURCE, SANE_TITLE_SCAN_SOURCE,
 | |
|     SANE_DESC_SCAN_SOURCE, (SANE_String_Const*)s->dev->info.scansources,
 | |
|     OPT_SCANSOURCE, 0);
 | |
| 
 | |
|   if (i < 2)
 | |
|     s->opt[OPT_SCANSOURCE].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     init_string_option(s, "Paper size", "Paper size",
 | |
|       "Paper size", paper_list_pcin500, OPT_PAPER, 0);
 | |
|   else
 | |
|     init_string_option(s, "Paper size", "Paper size",
 | |
|       "Paper size", paper_list_pcinxxx, OPT_PAPER, 1);
 | |
| 
 | |
|   /* gamma */
 | |
|   init_string_option(s, "Gamma", "Gamma", "Gamma", gamma_list, OPT_GAMMA, 0);
 | |
| 
 | |
|   /* Resolution Group */
 | |
|   s->opt[OPT_RESOLUTION_GROUP].title = "Resolution";
 | |
|   s->opt[OPT_RESOLUTION_GROUP].desc = "";
 | |
|   s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_RESOLUTION_GROUP].cap = 0;
 | |
|   s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
| #ifdef USE_RESOLUTION_LIST
 | |
|   /* select resolution */
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     init_string_option(s, "Resolution", "Resolution", "Resolution",
 | |
|       resolution_list_pcin500, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCIN500);
 | |
|   else
 | |
|     init_string_option(s, "Resolution", "Resolution", "Resolution",
 | |
|       resolution_list_pcinxxx, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCINXXX);
 | |
| #endif
 | |
| 
 | |
|   /* x & y resolution */
 | |
|   s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|       s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION"(x 10)";
 | |
|   else
 | |
|       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_RANGE;
 | |
|   s->opt[OPT_RESOLUTION].constraint.range = &s->dev->info.res_range;
 | |
|   s->val[OPT_RESOLUTION].w = s->dev->info.res_default;
 | |
| 
 | |
|   /* "Geometry" group: */
 | |
|   s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
 | |
|   s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | |
|   s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* 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->dev->info.tl_x_ranges[default_source];
 | |
|   s->val[OPT_TL_X].w = s->dev->info.tl_x_ranges[default_source].min;
 | |
| 
 | |
|   /* 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->dev->info.tl_y_ranges[default_source];
 | |
|   s->val[OPT_TL_Y].w = s->dev->info.tl_y_ranges[default_source].min;
 | |
| 
 | |
|   /* 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->dev->info.br_x_ranges[default_source];
 | |
|   scalar = s->dev->info.x_default;
 | |
|   clip_value (&s->opt[OPT_BR_X], &scalar);
 | |
|   s->val[OPT_BR_X].w = scalar;
 | |
| 
 | |
|   /* 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->dev->info.br_y_ranges[default_source];
 | |
|   scalar = s->dev->info.y_default;
 | |
|   clip_value (&s->opt[OPT_BR_X], &scalar);
 | |
|   s->val[OPT_BR_Y].w = scalar;
 | |
| 
 | |
|   /* "Enhancement" group: */
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* edge emphasis */
 | |
|   init_string_option(s, "Edge emphasis", "Edge emphasis",
 | |
|     "Edge emphasis", edge_emphasis_list,
 | |
|     OPT_EDGE_EMPHASIS, 0);
 | |
| 
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     s->opt[OPT_EDGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   /* OR */
 | |
|   s->opt[OPT_OR].name = "OR";
 | |
|   s->opt[OPT_OR].title = "OR";
 | |
|   s->opt[OPT_OR].desc = "Select OR emphancement";
 | |
|   s->opt[OPT_OR].type = SANE_TYPE_BOOL;
 | |
|   s->val[OPT_OR].w = SANE_FALSE;
 | |
| 
 | |
|   /* EDGE */
 | |
|   s->opt[OPT_EDGE].name = "edge";
 | |
|   s->opt[OPT_EDGE].title = "Edge";
 | |
|   s->opt[OPT_EDGE].desc = "Select Edge emphancement";
 | |
|   s->opt[OPT_EDGE].type = SANE_TYPE_BOOL;
 | |
|   s->val[OPT_EDGE].w = SANE_FALSE;
 | |
| 
 | |
|   /* NR */
 | |
|   s->opt[OPT_NR].name = "NR";
 | |
|   s->opt[OPT_NR].title = "NR";
 | |
|   s->opt[OPT_NR].desc = "Select noise reduction";
 | |
|   s->opt[OPT_NR].type = SANE_TYPE_BOOL;
 | |
|   s->val[OPT_NR].w = SANE_FALSE;
 | |
| 
 | |
|   if (s->dev->sensedat.model != PCIN500)
 | |
|     {
 | |
|       s->opt[OPT_EDGE].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_NR].cap |= SANE_CAP_INACTIVE;
 | |
|       s->opt[OPT_OR].cap |= SANE_CAP_INACTIVE;
 | |
|     }
 | |
|   /* tint */
 | |
|   s->opt[OPT_TINT].name = "tint";
 | |
|   s->opt[OPT_TINT].title = "Tint";
 | |
|   s->opt[OPT_TINT].desc = "Select tint";
 | |
|   s->opt[OPT_TINT].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_TINT].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_TINT].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TINT].constraint.range = &s->dev->info.tint_range;
 | |
|   s->val[OPT_TINT].w = 128;
 | |
|   if (s->dev->sensedat.model != PCIN500)
 | |
|     s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   /* color */
 | |
|   s->opt[OPT_COLOR].name = "color";
 | |
|   s->opt[OPT_COLOR].title = "Color";
 | |
|   s->opt[OPT_COLOR].desc = "Select color";
 | |
|   s->opt[OPT_COLOR].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_COLOR].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_COLOR].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_COLOR].constraint.range = &s->dev->info.color_range;
 | |
|   s->val[OPT_COLOR].w = 128;
 | |
|   if (s->dev->sensedat.model != PCIN500)
 | |
|     s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   /* 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 = &s->dev->info.threshold_range;
 | |
|   s->val[OPT_THRESHOLD].w = 128;
 | |
|   s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
|   s->opt[OPT_THRESHOLD_R].name = SANE_NAME_THRESHOLD "-red";
 | |
|   /* xxx the titles and decriptions are confusing:
 | |
|      "set white point (red)"
 | |
|      Any idea? maybe "threshold to get the red component on"
 | |
|   */
 | |
|   s->opt[OPT_THRESHOLD_R].title = SANE_TITLE_THRESHOLD " (red)";
 | |
|   s->opt[OPT_THRESHOLD_R].desc = SANE_DESC_THRESHOLD " (red)";
 | |
|   s->opt[OPT_THRESHOLD_R].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_THRESHOLD_R].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_THRESHOLD_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_THRESHOLD_R].constraint.range = &s->dev->info.threshold_range;
 | |
|   s->val[OPT_THRESHOLD_R].w = 128;
 | |
|   s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   s->opt[OPT_THRESHOLD_G].name = SANE_NAME_THRESHOLD "-green";
 | |
|   s->opt[OPT_THRESHOLD_G].title = SANE_TITLE_THRESHOLD " (green)";
 | |
|   s->opt[OPT_THRESHOLD_G].desc = SANE_DESC_THRESHOLD " (green)";
 | |
|   s->opt[OPT_THRESHOLD_G].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_THRESHOLD_G].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_THRESHOLD_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_THRESHOLD_G].constraint.range = &s->dev->info.threshold_range;
 | |
|   s->val[OPT_THRESHOLD_G].w = 128;
 | |
|   s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   s->opt[OPT_THRESHOLD_B].name = SANE_NAME_THRESHOLD "-blue";
 | |
|   s->opt[OPT_THRESHOLD_B].title = SANE_TITLE_THRESHOLD " (blue)";
 | |
|   s->opt[OPT_THRESHOLD_B].desc = SANE_DESC_THRESHOLD " (blue)";
 | |
|   s->opt[OPT_THRESHOLD_B].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_THRESHOLD_B].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_THRESHOLD_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_THRESHOLD_B].constraint.range = &s->dev->info.threshold_range;
 | |
|   s->val[OPT_THRESHOLD_B].w = 128;
 | |
|   s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
| #endif
 | |
| 
 | |
|   /* light color (for gray scale and line art scans) */
 | |
|   init_string_option(s, "LightColor", "LightColor", "LightColor",
 | |
|     light_color_list, OPT_LIGHTCOLOR, 3);
 | |
|   s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
 | |
| 
 | |
|   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].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
 | |
|   s->val[OPT_PREVIEW].w     = SANE_FALSE;
 | |
| 
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|   /* custom-gamma table */
 | |
|   s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
 | |
|   s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
 | |
|   s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
 | |
|   s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
 | |
|   s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
 | |
| 
 | |
|   /* grayscale gamma vector */
 | |
|   s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
 | |
|   s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
 | |
|   s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
 | |
|   s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
 | |
| #if 0
 | |
|   s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
|   s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
 | |
|   s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
 | |
|   s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
 | |
| 
 | |
|   /* red gamma vector */
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
 | |
| #if 0
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
 | |
|   s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
 | |
| 
 | |
|   /* green gamma vector */
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
 | |
| #if 0
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
 | |
|   s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
 | |
| 
 | |
|   /* blue gamma vector */
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
 | |
| #if 0
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
 | |
|   s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
 | |
|   set_gamma_caps(s);
 | |
| #endif
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| do_cancel (NEC_Scanner * s)
 | |
| {
 | |
|   DBG (10, "<< do_cancel ");
 | |
| 
 | |
| #ifdef USE_FORK
 | |
|   if (s->reader_pid > 0)
 | |
|     {
 | |
|       int exit_status;
 | |
|       int count = 0;
 | |
|       /* ensure child knows it's time to stop: */
 | |
| 
 | |
|       DBG(11, "stopping reader process\n");
 | |
|       s->rdr_ctl->cancel = 1;
 | |
|       while(reader_running(s) && count < 100)
 | |
|         {
 | |
|           usleep(100000);
 | |
|           count++;
 | |
|         };
 | |
|       if (reader_running(s))
 | |
|         {
 | |
|           kill(s->reader_pid, SIGKILL);
 | |
|         }
 | |
|       wait(&exit_status);
 | |
|       DBG(11, "reader process stopped\n");
 | |
| 
 | |
|       s->reader_pid = 0;
 | |
|     }
 | |
| 
 | |
| #endif
 | |
|   s->scanning = SANE_FALSE;
 | |
| 
 | |
|   if (s->fd >= 0)
 | |
|     {
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|     }
 | |
| #ifdef USE_FORK
 | |
|   {
 | |
|     struct shmid_ds ds;
 | |
|     if (s->shmid != -1)
 | |
|       shmctl(s->shmid, IPC_RMID, &ds);
 | |
|     s->shmid = -1;
 | |
|   }
 | |
| #endif
 | |
|   if (s->buffer)
 | |
|     free(s->buffer);
 | |
|   s->buffer = 0;
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_CANCELLED);
 | |
| }
 | |
| 
 | |
| static NEC_New_Device *new_devs = 0;
 | |
| static NEC_New_Device *new_dev_pool = 0;
 | |
| 
 | |
| static SANE_Status
 | |
| attach_and_list(const char *devnam)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   NEC_Device *devp;
 | |
|   NEC_New_Device *np;
 | |
| 
 | |
|   res = attach(devnam, &devp);
 | |
|   if (res == SANE_STATUS_GOOD)
 | |
|     {
 | |
|       if (new_dev_pool)
 | |
|         {
 | |
|           np = new_dev_pool;
 | |
|           new_dev_pool = np->next;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           np = malloc(sizeof(NEC_New_Device));
 | |
|           if (np == 0)
 | |
|             return SANE_STATUS_NO_MEM;
 | |
|         }
 | |
|       np->next =new_devs;
 | |
|       np->dev = devp;
 | |
|       new_devs = np;
 | |
|     }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
 | |
| {
 | |
|   char devnam[PATH_MAX] = "/dev/scanner";
 | |
|   char line[PATH_MAX];
 | |
|   const char *lp;
 | |
|   char *word;
 | |
|   char *end;
 | |
|   FILE *fp;
 | |
|   int opt_index = 0;
 | |
|   int buffers[2] = {DEFAULT_BUFFERS, DEFAULT_BUFFERS};
 | |
|   int bufsize[2] = {DEFAULT_BUFSIZE, DEFAULT_BUFSIZE};
 | |
|   int queued_reads[2] = {DEFAULT_QUEUED_READS, DEFAULT_QUEUED_READS};
 | |
|   int linecount = 0;
 | |
| #if 1
 | |
|   NEC_Device nd;
 | |
|   NEC_Device *dp = &nd;
 | |
| #else
 | |
|   NEC_Device *dp;
 | |
| #endif
 | |
|   NEC_New_Device *np;
 | |
|   int i;
 | |
| 
 | |
|   authorize = authorize; /* silence compilation warnings */
 | |
| 
 | |
|   DBG_INIT ();
 | |
|   DBG (10, "<< sane_init ");
 | |
| 
 | |
|   DBG (1, "sane_init: NEC (Ver %d.%d)\n", NEC_MAJOR, NEC_MINOR);
 | |
| 
 | |
|   if (version_code)
 | |
|     *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
 | |
| 
 | |
|   fp = sanei_config_open (NEC_CONFIG_FILE);
 | |
|   if (!fp)
 | |
|     {
 | |
|       /* use "/dev/scanner" as the default device name if no
 | |
|          config file is available
 | |
|       */
 | |
|       attach (devnam, &dp);
 | |
|       /* make sure that there are at least two buffers */
 | |
|       if (DEFAULT_BUFFERS < 2)
 | |
|         dp->info.buffers = DEFAULT_BUFFERS;
 | |
|       else
 | |
|         dp->info.buffers = 2;
 | |
|       dp->info.wanted_bufsize = DEFAULT_BUFSIZE;
 | |
|       dp->info.queued_reads = DEFAULT_QUEUED_READS;
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   while (fgets(line, PATH_MAX, fp))
 | |
|     {
 | |
|       linecount++;
 | |
|       word = 0;
 | |
|       lp = sanei_config_get_string(line, &word);
 | |
|       if (word)
 | |
|         {
 | |
|           if (word[0] != '#')
 | |
|             {
 | |
|               if (strcmp(word, "option") == 0)
 | |
|                 {
 | |
|                   free(word);
 | |
|                   word = 0;
 | |
|                   lp = sanei_config_get_string(lp, &word);
 | |
|                   if (strcmp(word, "buffers") == 0)
 | |
|                     {
 | |
|                       free(word);
 | |
|                       word = 0;
 | |
|                       sanei_config_get_string(lp, &word);
 | |
|                       i = strtol(word, &end, 0);
 | |
|                       if (end == word)
 | |
|                         {
 | |
|                           DBG(1, "error in config file, line %i: number expected:\n",
 | |
|                               linecount);
 | |
|                           DBG(1, "%s\n", line);
 | |
|                         }
 | |
|                       else
 | |
|                         if (i > 2)
 | |
|                           buffers[opt_index] = i;
 | |
|                         else
 | |
|                           buffers[opt_index] = 2;
 | |
|                     }
 | |
|                   else if (strcmp(word, "buffersize") == 0)
 | |
|                     {
 | |
|                       free(word);
 | |
|                       word = 0;
 | |
|                       sanei_config_get_string(lp, &word);
 | |
|                       i = strtol(word, &end, 0);
 | |
|                       if (word == end)
 | |
|                         {
 | |
|                           DBG(1, "error in config file, line %i: number expected:\n",
 | |
|                               linecount);
 | |
|                           DBG(1, "%s\n", line);
 | |
|                         }
 | |
|                       else
 | |
|                         bufsize[opt_index] = i;
 | |
|                     }
 | |
|                   else if (strcmp(word, "readqueue") == 0)
 | |
|                     {
 | |
|                       free(word);
 | |
|                       word = 0;
 | |
|                       sanei_config_get_string(lp, &word);
 | |
|                       i = strtol(word, &end, 0);
 | |
|                       if (word == end)
 | |
|                         {
 | |
|                           DBG(1, "error in config file, line %i: number expected:\n",
 | |
|                               linecount);
 | |
|                           DBG(1, "%s\n", line);
 | |
|                         }
 | |
|                       else
 | |
|                         queued_reads[opt_index] = i;
 | |
|                     }
 | |
|                   else
 | |
|                     {
 | |
|                       DBG(1, "error in config file, line %i: unknown option\n",
 | |
|                           linecount);
 | |
|                       DBG(1, "%s\n", line);
 | |
|                     }
 | |
|                 }
 | |
|               else
 | |
|                 {
 | |
|                   while (new_devs)
 | |
|                     {
 | |
|                       if (buffers[1] >= 2)
 | |
|                         new_devs->dev->info.buffers = buffers[1];
 | |
|                       else
 | |
|                         new_devs->dev->info.buffers = 2;
 | |
|                       if (bufsize[1] > 0)
 | |
|                         new_devs->dev->info.wanted_bufsize = bufsize[1];
 | |
|                       else
 | |
|                         new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
 | |
|                       if (queued_reads[1] >= 0)
 | |
|                         new_devs->dev->info.queued_reads = queued_reads[1];
 | |
|                       else
 | |
|                         new_devs->dev->info.queued_reads = 0;
 | |
|                       np = new_devs->next;
 | |
|                       new_devs->next = new_dev_pool;
 | |
|                       new_dev_pool = new_devs;
 | |
|                       new_devs = np;
 | |
|                     }
 | |
|                   if (line[strlen(line)-1] == '\n')
 | |
|                     line[strlen(line)-1] = 0;
 | |
|                   sanei_config_attach_matching_devices(line, &attach_and_list);
 | |
|                   buffers[1] = buffers[0];
 | |
|                   bufsize[1] = bufsize[0];
 | |
|                   queued_reads[1] = queued_reads[0];
 | |
|                   opt_index = 1;
 | |
|                 }
 | |
|             }
 | |
|           if (word) free(word);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   while (new_devs)
 | |
|     {
 | |
|       if (buffers[1] >= 2)
 | |
|         new_devs->dev->info.buffers = buffers[1];
 | |
|       else
 | |
|         new_devs->dev->info.buffers = 2;
 | |
|       if (bufsize[1] > 0)
 | |
|         new_devs->dev->info.wanted_bufsize = bufsize[1];
 | |
|       else
 | |
|         new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE;
 | |
|       if (queued_reads[1] >= 0)
 | |
|         new_devs->dev->info.queued_reads = queued_reads[1];
 | |
|       else
 | |
|         new_devs->dev->info.queued_reads = 0;
 | |
|       if (line[strlen(line)-1] == '\n')
 | |
|         line[strlen(line)-1] = 0;
 | |
|       np = new_devs->next;
 | |
|       free(new_devs);
 | |
|       new_devs = np;
 | |
|     }
 | |
|   while (new_dev_pool)
 | |
|     {
 | |
|       np = new_dev_pool->next;
 | |
|       free(new_dev_pool);
 | |
|       new_dev_pool = np;
 | |
|     }
 | |
|   fclose(fp);
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_GOOD);
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_exit (void)
 | |
| {
 | |
|   NEC_Device *dev, *next;
 | |
|   DBG (10, "<< sane_exit ");
 | |
| 
 | |
|   for (dev = first_dev; dev; dev = next)
 | |
|     {
 | |
|       next = dev->next;
 | |
|       free ((void *) dev->sane.name);
 | |
|       free ((void *) dev->sane.model);
 | |
|       free (dev);
 | |
|     }
 | |
|   first_dev = 0;
 | |
| 
 | |
|   if (devlist)
 | |
|     free(devlist);
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
 | |
| {
 | |
|   NEC_Device *dev;
 | |
|   int i;
 | |
|   DBG (10, "<< sane_get_devices ");
 | |
| 
 | |
|   local_only = local_only; /* silence compilation warnings */
 | |
| 
 | |
|   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;
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_open (SANE_String_Const devnam, SANE_Handle * handle)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   NEC_Device *dev;
 | |
|   NEC_Scanner *s;
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|   int i, j;
 | |
| #endif
 | |
| 
 | |
|   DBG (10, "<< sane_open ");
 | |
| 
 | |
|   if (devnam[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->dev = dev;
 | |
| 
 | |
|   s->buffer = 0;
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|   for (i = 0; i < 4; ++i)
 | |
|     for (j = 0; j < 256; ++j)
 | |
|       s->gamma_table[i][j] = j;
 | |
| #endif
 | |
|   status = init_options (s);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       /* xxx clean up mallocs */
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   s->next = first_handle;
 | |
|   first_handle = s;
 | |
| 
 | |
|   *handle = s;
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_close (SANE_Handle handle)
 | |
| {
 | |
|   NEC_Scanner *s = (NEC_Scanner *) handle;
 | |
|   DBG (10, "<< sane_close ");
 | |
| 
 | |
|   if (s->fd != -1)
 | |
|     sanei_scsi_close (s->fd);
 | |
| #ifdef USE_FORK
 | |
|   {
 | |
|     struct shmid_ds ds;
 | |
|     if (s->shmid != -1)
 | |
|       shmctl(s->shmid, IPC_RMID, &ds);
 | |
|   }
 | |
| #endif
 | |
|   if (s->buffer)
 | |
|     free(s->buffer);
 | |
|   free (s);
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
| }
 | |
| 
 | |
| const SANE_Option_Descriptor *
 | |
| sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | |
| {
 | |
|   NEC_Scanner *s = handle;
 | |
|   DBG (10, "<< sane_get_option_descriptor ");
 | |
| 
 | |
|   if ((unsigned) option >= NUM_OPTIONS)
 | |
|     return (0);
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (s->opt + option);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_control_option (SANE_Handle handle, SANE_Int option,
 | |
| 		     SANE_Action action, void *val, SANE_Int * info)
 | |
| {
 | |
|   NEC_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|   SANE_Word w, cap;
 | |
| #else
 | |
|   SANE_Word cap;
 | |
| #endif
 | |
|   int range_index;
 | |
|   DBG (10, "<< sane_control_option %i", option);
 | |
| 
 | |
|   if (info)
 | |
|     *info = 0;
 | |
| 
 | |
|   if (s->scanning)
 | |
|     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)
 | |
|     {
 | |
|       switch (option)
 | |
| 	{
 | |
| 	  /* word options: */
 | |
| 	case OPT_RESOLUTION:
 | |
| 	case OPT_TL_X:
 | |
| 	case OPT_TL_Y:
 | |
| 	case OPT_BR_X:
 | |
| 	case OPT_BR_Y:
 | |
| 	case OPT_NUM_OPTS:
 | |
| 	case OPT_THRESHOLD:
 | |
| 	case OPT_TINT:
 | |
| 	case OPT_COLOR:
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| 	case OPT_THRESHOLD_R:
 | |
| 	case OPT_THRESHOLD_G:
 | |
| 	case OPT_THRESHOLD_B:
 | |
| #endif
 | |
| 	case OPT_OR:
 | |
| 	case OPT_NR:
 | |
| 	case OPT_EDGE:
 | |
| 	case OPT_PREVIEW:
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| 	case OPT_CUSTOM_GAMMA:
 | |
| #endif
 | |
| 	  *(SANE_Word *) val = s->val[option].w;
 | |
| #if 0 /* here, values are read; reload should not be necessary */
 | |
| 	  if (info)
 | |
| 	    *info |= SANE_INFO_RELOAD_PARAMS;
 | |
| #endif
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| 	  /* word-array options: */
 | |
| 	case OPT_GAMMA_VECTOR:
 | |
| 	case OPT_GAMMA_VECTOR_R:
 | |
| 	case OPT_GAMMA_VECTOR_G:
 | |
| 	case OPT_GAMMA_VECTOR_B:
 | |
| 	  memcpy (val, s->val[option].wa, s->opt[option].size);
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| #endif
 | |
| 
 | |
| 	  /* string options: */
 | |
| 	case OPT_MODE:
 | |
| 	case OPT_HALFTONE:
 | |
| 	case OPT_PAPER:
 | |
| 	case OPT_GAMMA:
 | |
| #ifdef USE_RESOLUTION_LIST
 | |
| 	case OPT_RESOLUTION_LIST:
 | |
| #endif
 | |
| 	case OPT_EDGE_EMPHASIS:
 | |
| 	case OPT_LIGHTCOLOR:
 | |
| 	case OPT_SCANSOURCE:
 | |
| 	  strcpy (val, s->val[option].s);
 | |
| #if 0
 | |
| 	  if (info)
 | |
| 	    *info |= SANE_INFO_RELOAD_PARAMS;
 | |
| #endif
 | |
| 
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| 	}
 | |
|     }
 | |
|   else if (action == SANE_ACTION_SET_VALUE)
 | |
|     {
 | |
|       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_RESOLUTION:
 | |
| 	case OPT_TL_X:
 | |
| 	case OPT_TL_Y:
 | |
| 	case OPT_BR_X:
 | |
| 	case OPT_BR_Y:
 | |
| 	  if (info && s->val[option].w != *(SANE_Word *) val)
 | |
| 	    *info |= SANE_INFO_RELOAD_PARAMS;
 | |
|           // fall through
 | |
| 	case OPT_NUM_OPTS:
 | |
| 	case OPT_THRESHOLD:
 | |
| 	  /* xxx theoretically, we could use OPT_THRESHOLD in
 | |
| 	     bi-level color mode to adjust all three other
 | |
| 	     threshold together. But this would require to set
 | |
| 	     the bit SANE_INFO_RELOAD_OPTIONS in *info, and that
 | |
| 	     would unfortunately cause a crash in both xscanimage
 | |
| 	     and xsane... Therefore, OPT_THRESHOLD is disabled
 | |
| 	     for bi-level color scan right now.
 | |
| 	  */
 | |
| 	case OPT_TINT:
 | |
| 	case OPT_COLOR:
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| 	case OPT_THRESHOLD_R:
 | |
| 	case OPT_THRESHOLD_G:
 | |
| 	case OPT_THRESHOLD_B:
 | |
| #endif
 | |
| 	case OPT_OR:
 | |
| 	case OPT_NR:
 | |
| 	case OPT_EDGE:
 | |
| 	case OPT_PREVIEW:
 | |
| 	  s->val[option].w = *(SANE_Word *) val;
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| 	case OPT_MODE:
 | |
| 	  if (strcmp (val, M_LINEART) == 0)
 | |
| 	    {
 | |
| 	      s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| 	      s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
| 	      if (s->dev->sensedat.model == PCIN500)
 | |
|                 s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	    }
 | |
| 	  else if (strcmp (val, M_LINEART_COLOR) == 0)
 | |
| 	    {
 | |
| 	      s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| 	      s->opt[OPT_THRESHOLD_R].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_G].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_B].cap &= ~SANE_CAP_INACTIVE;
 | |
| #endif
 | |
| 	      if (s->dev->sensedat.model == PCIN500)
 | |
|                 s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	    }
 | |
| 	  else if (strcmp (val, M_GRAY) == 0)
 | |
| 	    {
 | |
| 	      s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE;
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| 	      s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
|               s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_TINT].cap &= ~SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_COLOR].cap &= ~SANE_CAP_INACTIVE;
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| 	      s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE;
 | |
| 	      s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE;
 | |
| #endif
 | |
|               s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
 | |
|             }
 | |
| #if 0
 | |
| 	  if (   strcmp (val, M_LINEART) == 0
 | |
| 	      || strcmp (val, M_GRAY) == 0)
 | |
|             {
 | |
| 	      s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE;
 | |
|             }
 | |
|           else
 | |
|             {
 | |
| 	      s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE;
 | |
|             }
 | |
| #endif
 | |
|           strcpy(s->val[option].s, val);
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|           set_gamma_caps(s);
 | |
| #endif
 | |
|           if (info)
 | |
| 	    *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| 	case OPT_GAMMA:
 | |
| 	case OPT_HALFTONE:
 | |
| 	case OPT_EDGE_EMPHASIS:
 | |
| 	case OPT_LIGHTCOLOR:
 | |
| #if 0
 | |
| 	  if (s->val[option].s)
 | |
| 	    free (s->val[option].s);
 | |
| 	  s->val[option].s = strdup (val);
 | |
| #endif
 | |
|           strcpy(s->val[option].s, val);
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| 	case OPT_SCANSOURCE:
 | |
| 	  if (info && strcmp (s->val[option].s, (SANE_String) val))
 | |
| 	    *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
 | |
| #if 0
 | |
| 	  if (s->val[option].s)
 | |
| 	    free (s->val[option].s);
 | |
| 	  s->val[option].s = strdup (val);
 | |
| #endif
 | |
|           strcpy(s->val[option].s, val);
 | |
| 	  if (strcmp(val, use_fsu) == 0)
 | |
| 	    range_index = SCAN_WITH_FSU;
 | |
| 	  else if (strcmp(val, use_adf) == 0)
 | |
| 	    range_index = SCAN_WITH_ADF;
 | |
| 	  else
 | |
| 	    range_index = SCAN_SIMPLE;
 | |
| 
 | |
|           s->opt[OPT_TL_X].constraint.range
 | |
|             = &s->dev->info.tl_x_ranges[range_index];
 | |
|           clip_value (&s->opt[OPT_TL_X], &s->val[OPT_TL_X].w);
 | |
| 
 | |
|           s->opt[OPT_TL_Y].constraint.range
 | |
|             = &s->dev->info.tl_y_ranges[range_index];
 | |
|           clip_value (&s->opt[OPT_TL_Y], &s->val[OPT_TL_Y].w);
 | |
| 
 | |
|           s->opt[OPT_BR_X].constraint.range
 | |
|             = &s->dev->info.br_x_ranges[range_index];
 | |
|           clip_value (&s->opt[OPT_BR_X], &s->val[OPT_BR_X].w);
 | |
| 
 | |
|           s->opt[OPT_BR_Y].constraint.range
 | |
|             = &s->dev->info.br_y_ranges[range_index];
 | |
|           clip_value (&s->opt[OPT_BR_Y], &s->val[OPT_BR_Y].w);
 | |
| 
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| 	case OPT_PAPER:
 | |
|           if (info)
 | |
| 	    *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
 | |
| #if 0
 | |
| 	  if (s->val[option].s)
 | |
| 	    free (s->val[option].s);
 | |
| 	  s->val[option].s = strdup (val);
 | |
| #endif
 | |
|           strcpy(s->val[option].s, val);
 | |
| 	  s->val[OPT_TL_X].w = SANE_FIX(0);
 | |
| 	  s->val[OPT_TL_Y].w = SANE_FIX(0);
 | |
| 	  if (strcmp (s->val[option].s, "A3") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(297);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(420);
 | |
| 	  }else if (strcmp (s->val[option].s, "A4") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(210);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(297);
 | |
| 	  }else if (strcmp (s->val[option].s, "A5") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(148.5);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(210);
 | |
| 	  }else if (strcmp (s->val[option].s, "A6") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(105);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(148.5);
 | |
| 	  }else if (strcmp (s->val[option].s, "B4") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(250);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(353);
 | |
| 	  }else if (strcmp (s->val[option].s, "B5") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(182);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(257);
 | |
| 	  }else if (strcmp (s->val[option].s, W_LETTER) == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(279.4);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(431.8);
 | |
| 	  }else if (strcmp (s->val[option].s, "Legal") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(215.9);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(355.6);
 | |
| 	  }else if (strcmp (s->val[option].s, "Letter") == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(215.9);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(279.4);
 | |
| 	  }else if (strcmp (s->val[option].s, INVOICE) == 0){
 | |
| 	      s->val[OPT_BR_X].w = SANE_FIX(215.9);
 | |
| 	      s->val[OPT_BR_Y].w = SANE_FIX(139.7);
 | |
| 	  }else{
 | |
| 	  }
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| 
 | |
| #ifdef USE_RESOLUTION_LIST
 | |
| 	case OPT_RESOLUTION_LIST:
 | |
| 	  if (info)
 | |
| 	    *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
 | |
| #if 0
 | |
| 	  if (s->val[option].s)
 | |
| 	    free (s->val[option].s);
 | |
| 	  s->val[option].s = strdup (val);
 | |
| #endif
 | |
| 	  for (i = 0; s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]; i++) {
 | |
| 	    if (strcmp (val,
 | |
| 	          s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]) == 0){
 | |
| 	      s->val[OPT_RESOLUTION].w
 | |
| 	        = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]);
 | |
| 	      if (info)
 | |
| 	        *info |= SANE_INFO_RELOAD_PARAMS;
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  }
 | |
| 	  return (SANE_STATUS_GOOD);
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| 	  /* side-effect-free word-array options: */
 | |
| 	case OPT_GAMMA_VECTOR:
 | |
| 	case OPT_GAMMA_VECTOR_R:
 | |
| 	case OPT_GAMMA_VECTOR_G:
 | |
| 	case OPT_GAMMA_VECTOR_B:
 | |
| 	  memcpy (s->val[option].wa, val, s->opt[option].size);
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 
 | |
| 	case OPT_CUSTOM_GAMMA:
 | |
| 	  w = *(SANE_Word *) val;
 | |
| 
 | |
| 	  if (w == s->val[OPT_CUSTOM_GAMMA].w)
 | |
| 	    return SANE_STATUS_GOOD;		/* no change */
 | |
| 
 | |
| 	  if (info)
 | |
| 	    *info |= SANE_INFO_RELOAD_OPTIONS;
 | |
| 	  s->val[OPT_CUSTOM_GAMMA].w = w;
 | |
|           set_gamma_caps(s);
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| #endif
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_INVAL);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
 | |
| {
 | |
|   int width, length, res;
 | |
|   const char *mode;
 | |
|   NEC_Scanner *s = handle;
 | |
|   DBG (10, "<< sane_get_parameters ");
 | |
| 
 | |
|   res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
 | |
|   if (!s->scanning)
 | |
|     {
 | |
|       /* make best-effort guess at what parameters will look like once
 | |
|          scanning starts.  */
 | |
|       memset (&s->params, 0, sizeof (s->params));
 | |
| 
 | |
|       width = MM_TO_PIX(  SANE_UNFIX(s->val[OPT_BR_X].w)
 | |
|                         - SANE_UNFIX(s->val[OPT_TL_X].w),
 | |
| 			s->dev->info.mud);
 | |
|       length = MM_TO_PIX(  SANE_UNFIX(s->val[OPT_BR_Y].w)
 | |
|                           - SANE_UNFIX(s->val[OPT_TL_Y].w),
 | |
| 			 s->dev->info.mud);
 | |
| 
 | |
|       s->width = width;
 | |
|       s->length = length;
 | |
|       s->params.pixels_per_line = width * res / s->dev->info.mud;
 | |
|       s->params.lines = length * res / s->dev->info.mud;
 | |
| 
 | |
|       if (s->dev->sensedat.model == PCIN500)
 | |
| 	{
 | |
| 	  s->params.pixels_per_line += 1;
 | |
| 	  s->params.lines += 1;
 | |
| 	}
 | |
|       s->unscanned_lines = s->params.lines;
 | |
|     }
 | |
| #if 0
 | |
|   else
 | |
|     {
 | |
|       buffer_status bs;
 | |
|       SANE_Status status;
 | |
|       size_t len = sizeof (buffer_status);
 | |
|       status = get_data_buffer_status (s->fd, &bs, &len);
 | |
|       DBG (11, "<< get_data_buffer_status ");
 | |
| 
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  do_cancel(s);
 | |
| 	  return (status);
 | |
| 	}
 | |
|       DBG (11, ">>\n ");
 | |
|       {
 | |
| #ifdef DEBUG_NEC
 | |
| 	int i;
 | |
| 	u_char *buf = &bs;
 | |
| 	DBG(11, "get data buffer status(debug):\n");
 | |
| 	for (i = 0; i < len; i += 16)
 | |
| 	  {
 | |
| 	    DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x \n",
 | |
| 		buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7], buf[i+8],
 | |
| 		buf[i+9], buf[i+10], buf[i+11]);
 | |
| 	  }
 | |
| #endif
 | |
|       }
 | |
|     }
 | |
| #endif
 | |
|   res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
 | |
| 
 | |
|   mode = s->val[OPT_MODE].s;
 | |
| 
 | |
|   if (strcmp (mode, M_LINEART) == 0)
 | |
|      {
 | |
|        s->params.format = SANE_FRAME_GRAY;
 | |
|        s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
 | |
|        s->params.depth = 1;
 | |
|        s->modes = MODES_LINEART;
 | |
|      }
 | |
|   else if (strcmp (mode, M_GRAY) == 0)
 | |
|      {
 | |
|        s->params.format = SANE_FRAME_GRAY;
 | |
|        s->params.bytes_per_line = s->params.pixels_per_line;
 | |
|        s->params.depth = 8;
 | |
|        s->modes = MODES_GRAY;
 | |
|      }
 | |
|   else if (strcmp (mode, M_LINEART_COLOR) == 0)
 | |
|     {
 | |
|        s->params.format = SANE_FRAME_RGB;
 | |
|        s->params.bytes_per_line = 3 * (s->params.pixels_per_line + 7) / 8;
 | |
|        s->params.depth = 8;
 | |
|        s->modes = MODES_LINEART_COLOR;
 | |
|     }
 | |
|   else
 | |
|      {
 | |
|        s->params.format = SANE_FRAME_RGB;
 | |
|        s->params.bytes_per_line = 3 * s->params.pixels_per_line;
 | |
|        s->params.depth = 8;
 | |
|        s->modes = MODES_COLOR;
 | |
|      }
 | |
|   s->params.last_frame = SANE_TRUE;
 | |
| 
 | |
|   if (params)
 | |
|     *params = s->params;
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_GOOD);
 | |
| }
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| 
 | |
| static int
 | |
| sprint_gamma(Option_Value val, SANE_Byte *dst)
 | |
| {
 | |
|   int i;
 | |
|   SANE_Byte *p = dst;
 | |
| 
 | |
|   p += sprintf((char *) p, "%i", val.wa[0]);
 | |
|   for (i = 1; i < 256; i++)
 | |
|     p += sprintf((char *) p, ",%i", val.wa[i] > 255 ? 255 : val.wa[i]);
 | |
|   return p - dst;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| send_ascii_gamma_tables (NEC_Scanner *s)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   int i;
 | |
| 
 | |
|   DBG(11, "<< send_ascii_gamma_tables ");
 | |
| 
 | |
|   /* we need: 4 bytes for each gamma value (3 digits + delimiter)
 | |
|              + 10 bytes for the command header
 | |
|      i.e. 4 * 4 * 256 + 10 = 4106 bytes
 | |
|   */
 | |
| 
 | |
|   if (s->dev->info.bufsize < 4106)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   memset(s->buffer, 0, 4106);
 | |
| 
 | |
|   i = sprint_gamma(s->val[OPT_GAMMA_VECTOR_R], &s->buffer[10]);
 | |
|   s->buffer[10+i++] = '/';
 | |
|   i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_G], &s->buffer[10+i]);
 | |
|   s->buffer[10+i++] = '/';
 | |
|   i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_B], &s->buffer[10+i]);
 | |
|   s->buffer[10+i++] = '/';
 | |
|   i += sprint_gamma(s->val[OPT_GAMMA_VECTOR], &s->buffer[10+i]);
 | |
| 
 | |
|   DBG(12, "%s\n", &s->buffer[10]);
 | |
| 
 | |
|   s->buffer[0] = SEND;
 | |
|   s->buffer[2] = 0x03;
 | |
|   s->buffer[7] = i >> 8;
 | |
|   s->buffer[8] = i & 0xff;
 | |
| 
 | |
|   wait_ready(s->fd);
 | |
|   status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
 | |
| 
 | |
|   DBG(11, ">>\n");
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static SANE_Status
 | |
| send_binary_g_table(NEC_Scanner *s, SANE_Word *a, int dtq)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   unsigned int i, j;
 | |
| 
 | |
|   dtq = dtq; /* silence compilation warnings */
 | |
| 
 | |
|   DBG(11, "<< send_binary_g_table\n");
 | |
| 
 | |
|   i = 256;
 | |
|   if (s->dev->info.bufsize < i)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
|   memset(s->buffer, 0, i+10);
 | |
| 
 | |
|   s->buffer[0] = SEND;
 | |
|   s->buffer[2] = 0x03;
 | |
|   s->buffer[7] = i >> 8;
 | |
|   s->buffer[8] = i & 0xff;
 | |
| 
 | |
|   for (i = 0; i < 256; i++)
 | |
|     {
 | |
|       s->buffer[i+11] = a[i&0xff] & 0xff;
 | |
|     }
 | |
| 
 | |
|   for (j = 0; j < 256; j += 16)
 | |
|     {
 | |
|       DBG(11, "%02x %02x %02x %02x %02x %02x %02x %02x "
 | |
|               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
 | |
|               a[j  ], a[j+1], a[j+2], a[j+3],
 | |
|               a[j+4], a[j+5], a[j+6], a[j+7],
 | |
|               a[j+8], a[j+9], a[j+10], a[j+11],
 | |
|               a[j+12], a[j+13], a[j+14], a[j+15]);
 | |
|     }
 | |
|   DBG(12, "transfer length = %d\n", i);
 | |
|   DBG(12, "buffer[7] = %d\n", s->buffer[7]);
 | |
|   DBG(12, "buffer[8] = %d\n", s->buffer[8]);
 | |
| 
 | |
|   /*  wait_ready(s->fd); */
 | |
|   status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0);
 | |
| 
 | |
|   DBG(11, ">>\n");
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
| static SANE_Status
 | |
| send_binary_gamma_tables (NEC_Scanner *s)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR].wa, 0x10);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     return status;
 | |
|   DBG(11, "send_binary_gamma_tables\n");
 | |
| #if 0
 | |
|   status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_R].wa, 0x11);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     return status;
 | |
| 
 | |
|   status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_G].wa, 0x12);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     return status;
 | |
| 
 | |
|   status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_B].wa, 0x13);
 | |
| #endif
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| send_gamma_tables (NEC_Scanner *s)
 | |
| {
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     {
 | |
|       return send_binary_gamma_tables(s);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       return send_ascii_gamma_tables(s);
 | |
|     }
 | |
| 
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_COLOR_THRESHOLD
 | |
| /* not used? */
 | |
| #if 0
 | |
| static SANE_Status
 | |
| send_threshold_data(NEC_Scanner *s)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   SANE_Byte cmd[26] = {SEND, 0, 0x82, 0, 0, 0, 0, 0, 0, 0};
 | |
|   int len;
 | |
| 
 | |
|   memset(cmd, 0, sizeof(cmd));
 | |
|   /* maximum string length: 3 bytes for each number (they are
 | |
|      restricted to the range 0..255), 3 '/' and the null-byte,
 | |
|      total: 16 bytes.
 | |
|   */
 | |
|   len = sprintf((char *) &cmd[10], "%i/%i/%i/%i",
 | |
|                 s->val[OPT_THRESHOLD_R].w,
 | |
|                 s->val[OPT_THRESHOLD_G].w,
 | |
|                 s->val[OPT_THRESHOLD_B].w,
 | |
|                 s->val[OPT_THRESHOLD].w);
 | |
|   cmd[8] = len;
 | |
| 
 | |
|   wait_ready(s->fd);
 | |
|   status = sanei_scsi_cmd(s->fd, cmd, len + 10, 0, 0);
 | |
|   return status;
 | |
| }
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| SANE_Status
 | |
| sane_start (SANE_Handle handle)
 | |
| {
 | |
|   char *mode, *halftone, *gamma, *edge, *lightcolor, *adf_fsu;
 | |
|   NEC_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
|   size_t buf_size;
 | |
|   window_param wp;
 | |
| 
 | |
|   DBG (10, "<< sane_start ");
 | |
| 
 | |
|   /* First make sure we have a current parameter set.  Some of the
 | |
|      parameters will be overwritten below, but that's OK.  */
 | |
|   status = sane_get_parameters (s, 0);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     return status;
 | |
| 
 | |
|   s->dev->sensedat.complain_on_adf_error = 1;
 | |
| 
 | |
| #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | |
|   s->dev->info.bufsize = s->dev->info.wanted_bufsize;
 | |
|   if (s->dev->info.bufsize < 32 * 1024)
 | |
|     s->dev->info.bufsize = 32 * 1024;
 | |
|   {
 | |
|     int bsize = s->dev->info.bufsize;
 | |
|     status = sanei_scsi_open_extended (s->dev->sane.name, &s->fd,
 | |
|               &sense_handler, &s->dev->sensedat, &bsize);
 | |
|     s->dev->info.bufsize = bsize;
 | |
|   }
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "open of %s failed: %s\n",
 | |
|          s->dev->sane.name, sane_strstatus (status));
 | |
|       return (status);
 | |
|     }
 | |
| 
 | |
|   /* make sure that we got at least 32 kB. Even then, the scan will be
 | |
|      awfully slow.
 | |
| 
 | |
|   */
 | |
|   if (s->dev->info.bufsize < 32 * 1024)
 | |
|     {
 | |
|       sanei_scsi_close(s->fd);
 | |
|       s->fd = -1;
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| #else
 | |
|   status = sanei_scsi_open(s->dev->sane.name, &s->fd, &sense_handler,
 | |
|               &s->dev->sensedat);
 | |
|   if (s->dev->info.wanted_bufsize < sanei_scsi_max_request_size)
 | |
|     s->dev->info.bufsize = s->dev->info.wanted_bufsize;
 | |
|   else
 | |
|     s->dev->info.bufsize = sanei_scsi_max_request_size;
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "open of %s failed: %s\n",
 | |
|          s->dev->sane.name, sane_strstatus (status));
 | |
|       return (status);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   s->buffer = malloc(s->dev->info.bufsize);
 | |
|   if (!s->buffer) {
 | |
|     sanei_scsi_close(s->fd);
 | |
|     s->fd = -1;
 | |
|     free(s);
 | |
|     return SANE_STATUS_NO_MEM;
 | |
|   }
 | |
| 
 | |
| #ifdef USE_FORK
 | |
|   {
 | |
|     struct shmid_ds ds;
 | |
|     size_t n;
 | |
| 
 | |
|     s->shmid = shmget(IPC_PRIVATE,
 | |
|        sizeof(NEC_rdr_ctl)
 | |
|        + s->dev->info.buffers *
 | |
|          (sizeof(NEC_shmem_ctl) + s->dev->info.bufsize),
 | |
|        IPC_CREAT | 0600);
 | |
|     if (s->shmid == -1)
 | |
|       {
 | |
|         free(s->buffer);
 | |
|         s->buffer = 0;
 | |
|         sanei_scsi_close(s->fd);
 | |
|         s->fd = -1;
 | |
|         return SANE_STATUS_NO_MEM;
 | |
|       }
 | |
|     s->rdr_ctl = (NEC_rdr_ctl*) shmat(s->shmid, 0, 0);
 | |
|     if ((int)s->rdr_ctl == -1)
 | |
|      {
 | |
|        shmctl(s->shmid, IPC_RMID, &ds);
 | |
|        free(s->buffer);
 | |
|        s->buffer = 0;
 | |
|        sanei_scsi_close(s->fd);
 | |
|        s->fd = -1;
 | |
|        return SANE_STATUS_NO_MEM;
 | |
|      }
 | |
| 
 | |
|     s->rdr_ctl->buf_ctl = (NEC_shmem_ctl*) &s->rdr_ctl[1];
 | |
|     for (n = 0; n < s->dev->info.buffers; n++)
 | |
|       {
 | |
|         s->rdr_ctl->buf_ctl[n].buffer =
 | |
|           (SANE_Byte*) &s->rdr_ctl->buf_ctl[s->dev->info.buffers]
 | |
|             + n * s->dev->info.bufsize;
 | |
|       }
 | |
|   }
 | |
| #endif /* USE_FORK */
 | |
| 
 | |
|   DBG (5, "start: TEST_UNIT_READY\n");
 | |
|   status = test_unit_ready (s->fd);
 | |
| 
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "TEST UNIT READY failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| 
 | |
|   DBG (3, "start: sending MODE SELECT\n");
 | |
|   status = mode_select_mud (s->fd, s->dev->info.mud);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "start: MODE_SELECT6 failed\n");
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| 
 | |
|   mode = s->val[OPT_MODE].s;
 | |
|   halftone = s->val[OPT_HALFTONE].s;
 | |
|   gamma = s->val[OPT_GAMMA].s;
 | |
|   edge = s->val[OPT_EDGE_EMPHASIS].s;
 | |
|   lightcolor = s->val[OPT_LIGHTCOLOR].s;
 | |
|   adf_fsu = s->val[OPT_SCANSOURCE].s;
 | |
| 
 | |
|   if (s->val[OPT_PREVIEW].w == SANE_FALSE)
 | |
|     {
 | |
|       s->res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       s->res = 75;
 | |
|     }
 | |
|   s->ulx = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_X].w), s->dev->info.mud);
 | |
|   s->uly = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_Y].w), s->dev->info.mud);
 | |
|   s->threshold = s->val[OPT_THRESHOLD].w;
 | |
| 
 | |
|   if (strcmp (mode, M_LINEART_COLOR) == 0)
 | |
|     s->bpp = 1;
 | |
|   else
 | |
|     s->bpp = s->params.depth;
 | |
| 
 | |
|   s->adf_fsu_mode = SCAN_SIMPLE; /* default: scan without ADF and FSU */
 | |
| 
 | |
|   if (strcmp(adf_fsu, use_fsu) == 0)
 | |
|     s->adf_fsu_mode = SCAN_WITH_FSU;
 | |
|   else if (strcmp(adf_fsu, use_adf) == 0)
 | |
|     s->adf_fsu_mode = SCAN_WITH_ADF;
 | |
|   else if (strcmp(adf_fsu, use_adf) == 0)
 | |
|     s->adf_fsu_mode = SCAN_SIMPLE;
 | |
| 
 | |
|   /* halftone must not set to be zero PC-IN500 */
 | |
|   s->halftone = 0x01;
 | |
|   if (strcmp (mode, M_LINEART) == 0)
 | |
|     {
 | |
|       s->reverse = 0;
 | |
|       s->image_composition = 1;
 | |
|       if (strcmp(halftone, M_BILEVEL) == 0)
 | |
| 	{
 | |
| 	  s->image_composition = 0;
 | |
| 	  s->halftone = 0x01;
 | |
| 	}
 | |
|       else if (strcmp(halftone, M_DITHER1) == 0)
 | |
| 	s->halftone = 0x01;
 | |
|       else if (strcmp(halftone, M_DITHER2) == 0)
 | |
| 	s->halftone = 0x10;
 | |
|       else if (strcmp(halftone, M_DITHER3) == 0)
 | |
| 	s->halftone = 0x20;
 | |
|     }
 | |
|   else if (strcmp (mode, M_GRAY) == 0)
 | |
|     {
 | |
|       s->image_composition = 2;
 | |
|       s->reverse = 1;
 | |
|     }
 | |
|   else if (strcmp (mode, M_LINEART_COLOR) == 0)
 | |
|     {
 | |
|       s->reverse = 1;
 | |
|       s->image_composition = 4;
 | |
|       if (strcmp(halftone, M_BILEVEL) == 0)
 | |
| 	{
 | |
| 	  s->image_composition = 3;
 | |
| 	  s->halftone = 0x01;
 | |
| 	}
 | |
|       else if (strcmp(halftone, M_DITHER1) == 0)
 | |
| 	s->halftone = 0x01;
 | |
|       else if (strcmp(halftone, M_DITHER2) == 0)
 | |
| 	s->halftone = 0x10;
 | |
|       else if (strcmp(halftone, M_DITHER3) == 0)
 | |
| 	s->halftone = 0x20;
 | |
|     }
 | |
|   else if (strcmp (mode, M_COLOR) == 0)
 | |
|     {
 | |
|       s->image_composition = 5;
 | |
|       s->reverse = 1;
 | |
|     }
 | |
| 
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     {
 | |
|       s->or = s->val[OPT_OR].w;
 | |
|       s->nr = s->val[OPT_NR].w;
 | |
|       s->edge = s->val[OPT_EDGE].w;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       if (strcmp (edge, EDGE_NONE) == 0)
 | |
| 	s->edge = 0;
 | |
|       else if (strcmp (edge, EDGE_MIDDLE) == 0)
 | |
| 	s->edge = 1;
 | |
|       else if (strcmp (edge, EDGE_STRONG) == 0)
 | |
| 	s->edge = 2;
 | |
|       else if (strcmp (edge, EDGE_BLUR) == 0)
 | |
| 	s->edge = 3;
 | |
|     }
 | |
| 
 | |
|   s->lightcolor = 3;
 | |
|   if (strcmp(lightcolor, LIGHT_GREEN) == 0)
 | |
|     s->lightcolor = 0;
 | |
|   else if (strcmp(lightcolor, LIGHT_RED) == 0)
 | |
|     s->lightcolor = 1;
 | |
|   else if (strcmp(lightcolor, LIGHT_BLUE) == 0)
 | |
|     s->lightcolor = 2;
 | |
|   else if (strcmp(lightcolor, LIGHT_NONE) == 0)
 | |
|     s->lightcolor = 3;
 | |
| 
 | |
|   s->adf_scan = 0;
 | |
| 
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|   if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE)
 | |
|     {
 | |
| #endif
 | |
|       if (s->dev->sensedat.model == PCIN500)
 | |
| 	{
 | |
| 	  if (strcmp (gamma, CRT1))
 | |
| 	    s->gamma = 1;
 | |
| 	  else if (strcmp (gamma, CRT2))
 | |
| 	    s->gamma = 2;
 | |
| 	  else if (strcmp (gamma, PRINTER1))
 | |
| 	    s->gamma = 3;
 | |
| 	  else if (strcmp (gamma, PRINTER2))
 | |
| 	    s->gamma = 4;
 | |
| 	  else if (strcmp (gamma, NONE))
 | |
| 	    s->gamma = 5;
 | |
| 	}
 | |
| #if 0
 | |
|       if (s->dev->sensedat.model != PCIN500)
 | |
|         {
 | |
|           ss.dtc = 0x03;
 | |
|           if (strcmp (gamma, GAMMA10) == 0)
 | |
| 	    ss.dtq = 0x01;
 | |
|           else
 | |
| 	    ss.dtq = 0x02;
 | |
|           ss.length = 0;
 | |
|           DBG (5, "start: SEND\n");
 | |
|           status = send (s->fd,  &ss);
 | |
|           if (status != SANE_STATUS_GOOD)
 | |
|             {
 | |
|               DBG (1, "send failed: %s\n", sane_strstatus (status));
 | |
|               sanei_scsi_close (s->fd);
 | |
|               s->fd = -1;
 | |
|               return (status);
 | |
|             }
 | |
|        }
 | |
|      else
 | |
| #else
 | |
|        {
 | |
|          int i;
 | |
|          SANE_Word gtbl[256];
 | |
| #if 0
 | |
|          if (strcmp (gamma, GAMMA10) == 0)
 | |
|            for (i = 0; i < 256; i++)
 | |
|              gtbl[i] = i;
 | |
|          else
 | |
| #endif
 | |
|            {
 | |
|              gtbl[0] = 0;
 | |
|              for (i = 1; i < 256; i++)
 | |
|                gtbl[i] = 255 * exp(0.45 * log(i/255.0));
 | |
|            }
 | |
|          send_binary_g_table(s, gtbl, 0x10);
 | |
|          send_binary_g_table(s, gtbl, 0x11);
 | |
|          send_binary_g_table(s, gtbl, 0x12);
 | |
|          send_binary_g_table(s, gtbl, 0x13);
 | |
|        }
 | |
| #endif /* DEBUG_NEC */
 | |
| #ifdef USE_CUSTOM_GAMMA
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       s->gamma = 9;
 | |
|       status = send_gamma_tables(s);
 | |
|     }
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   s->tint = s->val[OPT_TINT].w;
 | |
|   s->color = s->val[OPT_COLOR].w;
 | |
| 
 | |
|   memset (&wp, 0, sizeof (wp));
 | |
|   /* every NEC scanner seems to have a different
 | |
|      window descriptor block...
 | |
|   */
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     buf_size = sizeof(WDB) + sizeof(WDBX500);
 | |
|   else
 | |
|     buf_size = sizeof(WDB);
 | |
| 
 | |
|   wp.wpdh.wdl[0] = buf_size >> 8;
 | |
|   wp.wpdh.wdl[1] = buf_size;
 | |
|   wp.wdb.x_res[0] = s->res >> 8;
 | |
|   wp.wdb.x_res[1] = s->res;
 | |
|   wp.wdb.y_res[0] = s->res >> 8;
 | |
|   wp.wdb.y_res[1] = s->res;
 | |
|   wp.wdb.x_ul[0] = s->ulx >> 24;
 | |
|   wp.wdb.x_ul[1] = s->ulx >> 16;
 | |
|   wp.wdb.x_ul[2] = s->ulx >> 8;
 | |
|   wp.wdb.x_ul[3] = s->ulx;
 | |
|   wp.wdb.y_ul[0] = s->uly >> 24;
 | |
|   wp.wdb.y_ul[1] = s->uly >> 16;
 | |
|   wp.wdb.y_ul[2] = s->uly >> 8;
 | |
|   wp.wdb.y_ul[3] = s->uly;
 | |
|   wp.wdb.width[0] = s->width >> 24;
 | |
|   wp.wdb.width[1] = s->width >> 16;
 | |
|   wp.wdb.width[2] = s->width >> 8;
 | |
|   wp.wdb.width[3] = s->width;
 | |
|   wp.wdb.length[0] = s->length >> 24;
 | |
|   wp.wdb.length[1] = s->length >> 16;
 | |
|   wp.wdb.length[2] = s->length >> 8;
 | |
|   wp.wdb.length[3] = s->length;
 | |
|   wp.wdb.brightness = 0;
 | |
|   wp.wdb.threshold = s->threshold;
 | |
|   wp.wdb.brightness = 128;
 | |
|   wp.wdb.contrast = 128;
 | |
|   wp.wdb.image_composition = s->image_composition;
 | |
|   if (s->image_composition <= 2 || s->image_composition >= 5)
 | |
|     wp.wdb.bpp = s->bpp;
 | |
|   else
 | |
|     wp.wdb.bpp = 1;
 | |
|   wp.wdb.ht_pattern[0] = 0;
 | |
|   wp.wdb.ht_pattern[1] = 0;
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     wp.wdb.ht_pattern[1] = s->halftone;
 | |
|   wp.wdb.rif_padding = (s->reverse * 128) + 0;
 | |
| 
 | |
|   if (s->dev->sensedat.model == PCIN500)
 | |
|     {
 | |
|       wp.wdbx500.data_length = 0x07;
 | |
|       wp.wdbx500.control = 0 | (s->or << 7) | (s->edge << 6) | (s->nr << 5) |
 | |
| 	                   (s->lightcolor << 2);
 | |
|       wp.wdbx500.format = 0;
 | |
|       wp.wdbx500.gamma = s->gamma;
 | |
|       wp.wdbx500.tint = s->tint;
 | |
|       wp.wdbx500.color = s->color;
 | |
|       wp.wdbx500.reserved1 = 0;
 | |
|       wp.wdbx500.reserved2 = 0;
 | |
|     }
 | |
| 
 | |
|   DBG (5, "wdl=%d\n", (wp.wpdh.wdl[0] << 8) + wp.wpdh.wdl[1]);
 | |
|   DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
 | |
|   DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
 | |
|   DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
 | |
|                       (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
 | |
|   DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
 | |
|                       (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
 | |
|   DBG (5, "width=%d\n", (wp.wdb.width[0] << 8) + (wp.wdb.width[1] << 16) +
 | |
|                         (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
 | |
|   DBG (5, "length=%d\n", (wp.wdb.length[0] << 16) + (wp.wdb.length[1] << 16) +
 | |
|                          (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
 | |
| 
 | |
|   DBG (5, "threshold=%d\n", wp.wdb.threshold);
 | |
|   DBG (5, "image_composition=%d\n", wp.wdb.image_composition);
 | |
|   DBG (5, "bpp=%d\n", wp.wdb.bpp);
 | |
|   DBG (5, "rif_padding=%d\n", wp.wdb.rif_padding);
 | |
| 
 | |
| #ifdef DEBUG_NEC
 | |
|   {
 | |
|     window_param foo;
 | |
|     size_t len = buf_size;
 | |
|     len += sizeof(WPDH);
 | |
|   DBG (5, "start: GET WINDOW\n");
 | |
|   status = get_window (s->fd, &foo, &len);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| #if 1
 | |
|   {
 | |
|     unsigned char *p = (unsigned char*) &foo;
 | |
|     int i;
 | |
|     DBG(11, "get window(debug):\n");
 | |
|     for (i = 0; i < len; i += 16)
 | |
|      {
 | |
|       DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
 | |
|       p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
 | |
|       p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
 | |
|      }
 | |
|   }
 | |
| #endif
 | |
|   foo.wpdh.wpdh[1] = 0;
 | |
|   status = set_window (s->fd, &foo, len);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| #if 1
 | |
|   {
 | |
|     unsigned char *p = (unsigned char*) &foo;
 | |
|     int i;
 | |
|     DBG(11, "set window(debug):\n");
 | |
|     for (i = 0; i < len; i += 16)
 | |
|      {
 | |
|       DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
 | |
|       p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
 | |
|       p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
 | |
|      }
 | |
|   }
 | |
| #endif
 | |
|   }
 | |
| #endif /* debug_nec */
 | |
| 
 | |
| #ifdef DEBUG_NEC
 | |
|   {
 | |
|     unsigned char *p = (unsigned char*) ℘
 | |
|     int i;
 | |
|     DBG(11, "set window(debug):\n");
 | |
|     for (i = 0; i < sizeof(wp.wdb) + sizeof(wp.wdbx500); i += 16)
 | |
|      {
 | |
|       DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
 | |
|       p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
 | |
|       p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
 | |
|      }
 | |
|   }
 | |
| #endif /* debug_nec */
 | |
| 
 | |
|   buf_size += sizeof(WPDH);
 | |
|   DBG (5, "start: SET WINDOW\n");
 | |
|   status = set_window (s->fd, &wp, buf_size);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| 
 | |
|   memset (&wp, 0, buf_size);
 | |
|   DBG (5, "start: GET WINDOW\n");
 | |
|   status = get_window (s->fd, &wp, &buf_size);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       return (status);
 | |
|     }
 | |
| #ifdef DEBUG_NEC
 | |
|   {
 | |
|     unsigned char *p = (unsigned char*) ℘
 | |
|     int i;
 | |
|     DBG(11, "get window(debug):\n");
 | |
|     for (i = 0; i < buf_size; i += 16)
 | |
|      {
 | |
|       DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n",
 | |
|       p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8],
 | |
|       p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]);
 | |
|      }
 | |
|   }
 | |
| #endif
 | |
|   DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]);
 | |
|   DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]);
 | |
|   DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) +
 | |
|                       (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]);
 | |
|   DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) +
 | |
|        (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]);
 | |
|   DBG (5, "width=%d\n", (wp.wdb.width[0] << 24) + (wp.wdb.width[1] << 16) +
 | |
|                         (wp.wdb.width[2] << 8) + wp.wdb.width[3]);
 | |
|   DBG (5, "length=%d\n", (wp.wdb.length[0] << 24) + (wp.wdb.length[1] << 16) +
 | |
|                          (wp.wdb.length[2] << 8) + wp.wdb.length[3]);
 | |
|   DBG (5, "start: SCAN\n");
 | |
|   s->scanning = SANE_TRUE;
 | |
|   s->busy = SANE_TRUE;
 | |
|   s->cancel = SANE_FALSE;
 | |
|   s->get_params_called = 0;
 | |
| 
 | |
|   status = scan (s->fd);
 | |
| #ifdef DEBUG
 | |
|           {
 | |
|             struct timeval t;
 | |
|             gettimeofday(&t, 0);
 | |
|             DBG(2, "rd: scan started        %li.%06li\n", t.tv_sec, t.tv_usec);
 | |
|           }
 | |
| #endif
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
 | |
|       sanei_scsi_close (s->fd);
 | |
|       s->fd = -1;
 | |
|       s->busy = SANE_FALSE;
 | |
|       s->cancel = SANE_FALSE;
 | |
|       return (status);
 | |
|     }
 | |
| 
 | |
|   /* ask the scanner for the scan size */
 | |
|   /* wait_ready(s->fd); */
 | |
| #ifdef DEBUG
 | |
|           {
 | |
|             struct timeval t;
 | |
|             gettimeofday(&t, 0);
 | |
|             DBG(2, "rd: wait_ready ok       %li.%06li\n", t.tv_sec, t.tv_usec);
 | |
|           }
 | |
| #endif
 | |
|   sane_get_parameters(s, 0);
 | |
| #ifdef DEBUG
 | |
|           {
 | |
|             struct timeval t;
 | |
|             gettimeofday(&t, 0);
 | |
|             DBG(2, "rd: get_params ok       %li.%06li\n", t.tv_sec, t.tv_usec);
 | |
|           }
 | |
| #endif
 | |
|   if (strcmp (mode, M_LINEART_COLOR) != 0)
 | |
|     s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
 | |
|   else
 | |
|     {
 | |
|       s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
 | |
|     }
 | |
| 
 | |
| #ifdef USE_FORK
 | |
|   {
 | |
|     size_t i;
 | |
|     for (i = 0; i < s->dev->info.buffers; i++)
 | |
|       s->rdr_ctl->buf_ctl[i].shm_status = SHM_EMPTY;
 | |
|     s->read_buff = 0;
 | |
|     s->rdr_ctl->cancel = 0;
 | |
|     s->rdr_ctl->running = 0;
 | |
|     s->rdr_ctl->status  = SANE_STATUS_GOOD;
 | |
|   }
 | |
|   s->reader_pid = fork();
 | |
| #ifdef DEBUG
 | |
|           {
 | |
|             struct timeval t;
 | |
|             gettimeofday(&t, 0);
 | |
|             DBG(2, "rd: forked              %li.%06li %i\n", t.tv_sec, t.tv_usec,
 | |
|               s->reader_pid);
 | |
|           }
 | |
| #endif
 | |
|   if (s->reader_pid == 0)
 | |
|     {
 | |
|       sigset_t ignore_set;
 | |
|       struct SIGACTION act;
 | |
| 
 | |
|       sigfillset (&ignore_set);
 | |
|       sigdelset (&ignore_set, SIGTERM);
 | |
|       sigprocmask (SIG_SETMASK, &ignore_set, 0);
 | |
| 
 | |
|       memset (&act, 0, sizeof (act));
 | |
|       sigaction (SIGTERM, &act, 0);
 | |
| 
 | |
|       /* don't use exit() since that would run the atexit() handlers... */
 | |
|       _exit (reader_process (s));
 | |
|     }
 | |
|   else if (s->reader_pid == -1)
 | |
|     {
 | |
|       s->busy = SANE_FALSE;
 | |
|       do_cancel(s);
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
| #endif /* USE_FORK */
 | |
| 
 | |
| 
 | |
|   DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, "
 | |
|        "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
 | |
|        s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_RESOLUTION].w);
 | |
| 
 | |
|   s->busy = SANE_FALSE;
 | |
|   s->buf_used = 0;
 | |
|   s->buf_pos = 0;
 | |
| 
 | |
|   if (s->cancel == SANE_TRUE)
 | |
|     {
 | |
|       do_cancel(s);
 | |
|       DBG (10, ">>\n");
 | |
|       return(SANE_STATUS_CANCELLED);
 | |
|     }
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_GOOD);
 | |
| 
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| sane_read_direct (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
 | |
| 	   SANE_Int * len)
 | |
| {
 | |
|   NEC_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
|   size_t nread;
 | |
|   DBG (10, "<< sane_read_direct ");
 | |
| 
 | |
| #if 0
 | |
|   {
 | |
|     buffer_status bs;
 | |
|     size_t len = sizeof (buffer_status);
 | |
|     get_data_buffer_status (s->fd, &bs, &len);
 | |
|     DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]);
 | |
|   }
 | |
| #endif
 | |
|   DBG (20, "remaining: %lu ", (u_long)  s->bytes_to_read);
 | |
|   *len = 0;
 | |
| 
 | |
|   if (s->bytes_to_read == 0)
 | |
|     {
 | |
|       do_cancel (s);
 | |
|       return (SANE_STATUS_EOF);
 | |
|     }
 | |
| 
 | |
|   if (!s->scanning)
 | |
|     return (do_cancel (s));
 | |
|   nread = max_len;
 | |
|   if (nread > s->bytes_to_read)
 | |
|     nread = s->bytes_to_read;
 | |
|   if (nread > s->dev->info.bufsize)
 | |
|     nread = s->dev->info.bufsize;
 | |
| 
 | |
| #ifdef USE_FORK
 | |
|   status = read_data(s, dst_buf, &nread);
 | |
| #else
 | |
| #ifdef NOTUSE_PCIN500
 | |
|   wait_ready(s->fd);
 | |
| #endif
 | |
|   status = read_data (s, dst_buf, &nread);
 | |
| #endif
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       do_cancel (s);
 | |
|       return (SANE_STATUS_IO_ERROR);
 | |
|     }
 | |
|   *len = nread;
 | |
|   s->bytes_to_read -= nread;
 | |
|   DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read);
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_GOOD);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| sane_read_shuffled (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
 | |
| 	   SANE_Int * len, int eight_bit_data)
 | |
| {
 | |
|   NEC_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
|   SANE_Byte *dest, *red, *green, *blue, mask;
 | |
|   SANE_Int transfer;
 | |
|   size_t nread, ntest, pixel, max_pixel, line, max_line;
 | |
|   size_t start_input, bytes_per_line_in;
 | |
|   DBG (10, "<< sane_read_shuffled ");
 | |
| 
 | |
| #if 0
 | |
|   {
 | |
|     buffer_status bs;
 | |
|     size_t len = sizeof (buffer_status);
 | |
|     get_data_buffer_status (s->fd, &bs, &len);
 | |
|     DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]);
 | |
|   }
 | |
| #endif
 | |
|   *len = 0;
 | |
|   if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
 | |
|     {
 | |
|       do_cancel (s);
 | |
|       DBG (10, ">>\n");
 | |
|       return (SANE_STATUS_EOF);
 | |
|     }
 | |
| 
 | |
|   if (!s->scanning)
 | |
|     {
 | |
|       DBG (10, ">>\n");
 | |
|       return(do_cancel(s));
 | |
|     }
 | |
| 
 | |
|   if (s->buf_pos < s->buf_used)
 | |
|     {
 | |
|       transfer = s->buf_used - s->buf_pos;
 | |
|       if (transfer > max_len)
 | |
|         transfer = max_len;
 | |
| 
 | |
|       memcpy(dst_buf, &(s->buffer[s->buf_pos]), transfer);
 | |
|       s->buf_pos += transfer;
 | |
|       max_len -= transfer;
 | |
|       *len = transfer;
 | |
|     }
 | |
| 
 | |
|   while (max_len > 0 && s->bytes_to_read > 0)
 | |
|     {
 | |
|       if (eight_bit_data)
 | |
|         {
 | |
|           nread = s->dev->info.bufsize / s->params.bytes_per_line - 1;
 | |
|           nread *= s->params.bytes_per_line;
 | |
|           if (nread > s->bytes_to_read)
 | |
|             nread = s->bytes_to_read;
 | |
|           max_line = nread / s->params.bytes_per_line;
 | |
|           start_input = s->params.bytes_per_line;
 | |
|           bytes_per_line_in = s->params.bytes_per_line;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           bytes_per_line_in = (s->params.pixels_per_line + 7) / 8;
 | |
|           bytes_per_line_in *= 3;
 | |
|           max_line = s->params.bytes_per_line + bytes_per_line_in;
 | |
|           max_line = s->dev->info.bufsize / max_line;
 | |
|           nread = max_line * bytes_per_line_in;
 | |
|           if (nread > s->bytes_to_read)
 | |
|             {
 | |
|               nread = s->bytes_to_read;
 | |
|               max_line = nread / bytes_per_line_in;
 | |
|             }
 | |
|           start_input = s->dev->info.bufsize - nread;
 | |
|         }
 | |
|       ntest = nread;
 | |
| 
 | |
| #ifdef USE_FORK
 | |
|       status = read_data (s, &(s->buffer[start_input]), &nread);
 | |
| #else
 | |
|       status = read_data (s, &(s->buffer[start_input]), &nread);
 | |
| #endif
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
|         {
 | |
|           do_cancel (s);
 | |
|           DBG (10, ">>\n");
 | |
|           return (SANE_STATUS_IO_ERROR);
 | |
|         }
 | |
| 
 | |
|       if (nread != ntest)
 | |
|         {
 | |
|           /* if this happens, something is wrong in the input buffer
 | |
|              management...
 | |
|           */
 | |
|           DBG(1, "Warning: could not read an integral number of scan lines\n");
 | |
|           DBG(1, "         image will be scrambled\n");
 | |
|         }
 | |
| 
 | |
| 
 | |
|       s->buf_used = max_line * s->params.bytes_per_line;
 | |
|       s->buf_pos = 0;
 | |
|       s->bytes_to_read -= nread;
 | |
|       dest = s->buffer;
 | |
|       max_pixel = s->params.pixels_per_line;
 | |
| 
 | |
|       if (eight_bit_data)
 | |
|         for (line = 1; line <= max_line; line++)
 | |
|           {
 | |
|             red = &(s->buffer[line * s->params.bytes_per_line]);
 | |
|             green = &(red[max_pixel]);
 | |
|             blue = &(green[max_pixel]);
 | |
|             for (pixel = 0; pixel < max_pixel; pixel++)
 | |
|               {
 | |
|                 *dest++ = *red++;
 | |
|                 *dest++ = *green++;
 | |
|                 *dest++ = *blue++;
 | |
|               }
 | |
|           }
 | |
|       else
 | |
|         for (line = 0; line < max_line; line++)
 | |
|           {
 | |
|             red = &(s->buffer[start_input + line * bytes_per_line_in]);
 | |
|             green = &(red[(max_pixel+7)/8]);
 | |
|             blue = &(green[(max_pixel+7)/8]);
 | |
|             mask = 0x80;
 | |
|             for (pixel = 0; pixel < max_pixel; pixel++)
 | |
|               {
 | |
|                 *dest++ = (*red & mask)   ? 0xff : 0;
 | |
|                 *dest++ = (*green & mask) ? 0xff : 0;
 | |
|                 *dest++ = (*blue & mask)  ? 0xff : 0;
 | |
|                 mask = mask >> 1;
 | |
|                 if (mask == 0)
 | |
|                   {
 | |
|                     mask = 0x80;
 | |
|                     red++;
 | |
|                     green++;
 | |
|                     blue++;
 | |
|                   }
 | |
|               }
 | |
|           }
 | |
| 
 | |
|       transfer = max_len;
 | |
|       if (transfer > s->buf_used)
 | |
|         transfer = s->buf_used;
 | |
|       memcpy(&(dst_buf[*len]), s->buffer, transfer);
 | |
| 
 | |
|       max_len -= transfer;
 | |
|       s->buf_pos += transfer;
 | |
|       *len += transfer;
 | |
|     }
 | |
| 
 | |
|   if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used)
 | |
|     do_cancel (s);
 | |
|   DBG (10, ">>\n");
 | |
|   return (SANE_STATUS_GOOD);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_read (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len,
 | |
| 	   SANE_Int * len)
 | |
| {
 | |
|   NEC_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (10, "<< sane_read ");
 | |
|   s->busy = SANE_TRUE;
 | |
|   if (s->cancel == SANE_TRUE)
 | |
|     {
 | |
|       do_cancel(s);
 | |
|       *len = 0;
 | |
|       return (SANE_STATUS_CANCELLED);
 | |
|     }
 | |
| 
 | |
|   if (s->image_composition <= 2)
 | |
|     status = sane_read_direct(handle, dst_buf, max_len, len);
 | |
|   else if (s->image_composition <= 4)
 | |
|     status = sane_read_shuffled(handle, dst_buf, max_len, len, 0);
 | |
|   else if (s->dev->sensedat.model == PCIN500)
 | |
|     status = sane_read_direct(handle, dst_buf, max_len, len);
 | |
|   else
 | |
|     status = sane_read_shuffled(handle, dst_buf, max_len, len, 1);
 | |
| 
 | |
|   s->busy = SANE_FALSE;
 | |
|   if (s->cancel == SANE_TRUE)
 | |
|     {
 | |
|       do_cancel(s);
 | |
|       return (SANE_STATUS_CANCELLED);
 | |
|     }
 | |
| 
 | |
|   DBG (10, ">> \n");
 | |
|   return (status);
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_cancel (SANE_Handle handle)
 | |
| {
 | |
|   NEC_Scanner *s = handle;
 | |
|   DBG (10, "<< sane_cancel ");
 | |
| 
 | |
|   s->cancel = SANE_TRUE;
 | |
|   if (s->busy == SANE_FALSE)
 | |
|       do_cancel(s);
 | |
| 
 | |
|   DBG (10, ">>\n");
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | |
| {
 | |
|   handle = handle;
 | |
|   non_blocking = non_blocking; /* silence compilation warnings */
 | |
| 
 | |
|   DBG (10, "<< sane_set_io_mode");
 | |
|   DBG (10, ">>\n");
 | |
| 
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
 | |
| {
 | |
|   handle = handle;
 | |
|   fd = fd; /* silence compilation warnings */
 | |
| 
 | |
|   DBG (10, "<< sane_get_select_fd");
 | |
|   DBG (10, ">>\n");
 | |
| 
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| }
 |