kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1949 wiersze
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1949 wiersze
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | ||
| 
 | ||
|    Copyright (C) 1997, 1998, 2001, 2002, 2013 Franck Schnefra, Michel Roelofs,
 | ||
|    Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
 | ||
|    Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Mikael Magnusson,
 | ||
|    Max Ushakov, Andrew Goodbody, Oliver Schwartz and Kevin Charter
 | ||
| 
 | ||
|    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 is a component of the implementation of a backend for many
 | ||
|    of the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
 | ||
| 
 | ||
| 
 | ||
| /* $Id$
 | ||
|    SnapScan backend scsi command functions */
 | ||
| 
 | ||
| /* scanner scsi commands */
 | ||
| 
 | ||
| static SANE_Status download_firmware(SnapScan_Scanner * pss);
 | ||
| static SANE_Status wait_scanner_ready (SnapScan_Scanner * pss);
 | ||
| 
 | ||
| #include "snapscan-usb.h"
 | ||
| 
 | ||
| /* find model by SCSI ID string or USB vendor/product ID */
 | ||
| static SnapScan_Model snapscani_get_model_id(char* model_str, int fd, SnapScan_Bus bus_type)
 | ||
| {
 | ||
|     static char me[] = "snapscani_get_model_id";
 | ||
|     SnapScan_Model model_id = UNKNOWN;
 | ||
|     SANE_Word vendor_id, product_id;
 | ||
|     int i;
 | ||
| 
 | ||
|     DBG(DL_CALL_TRACE, "%s(%s, %d, %d)\n",me, model_str, fd, bus_type);
 | ||
|     for (i = 0;  i < known_scanners;  i++)
 | ||
|     {
 | ||
|         if (0 == strcasecmp (model_str, scanners[i].scsi_name))
 | ||
|         {
 | ||
|             model_id = scanners[i].id;
 | ||
|             break;
 | ||
|         }
 | ||
|     }
 | ||
|     /* Also test USB vendor and product ID numbers, since some USB models use
 | ||
|        identical model names.
 | ||
|     */
 | ||
|     if ((bus_type == USB) &&
 | ||
|         (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) == SANE_STATUS_GOOD))
 | ||
|     {
 | ||
|     	DBG(DL_MINOR_INFO,
 | ||
|             "%s: looking up scanner for ID 0x%04x,0x%04x.\n",
 | ||
|             me, vendor_id, product_id);
 | ||
|         for (i = 0; i < known_usb_scanners; i++)
 | ||
|         {
 | ||
|             if ((usb_scanners[i].vendor_id == vendor_id) &&
 | ||
|                 (usb_scanners[i].product_id == product_id))
 | ||
|             {
 | ||
|                 model_id = usb_scanners[i].id;
 | ||
|                 DBG(DL_MINOR_INFO, "%s: scanner identified\n", me);
 | ||
|                 break;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     return model_id;
 | ||
| }
 | ||
| 
 | ||
| /* a sensible sense handler, courtesy of Franck;
 | ||
|    the last argument is expected to be a pointer to the associated
 | ||
|    SnapScan_Scanner structure */
 | ||
| static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg)
 | ||
| {
 | ||
|     static char me[] = "sense_handler";
 | ||
|     SnapScan_Scanner *pss = (SnapScan_Scanner *) arg;
 | ||
|     u_char sense, asc, ascq;
 | ||
|     char *sense_str = NULL, *as_str = NULL;
 | ||
|     SANE_Status status = SANE_STATUS_GOOD;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s(%ld, %p, %p)\n", me, (long) scsi_fd,
 | ||
|          (void *) result, (void *) arg);
 | ||
| 
 | ||
|     sense = result[2] & 0x0f;
 | ||
|     asc = result[12];
 | ||
|     ascq = result[13];
 | ||
|     if (pss)
 | ||
|     {
 | ||
|         pss->asi1 = result[18];
 | ||
|         pss->asi2 = result[19];
 | ||
|     }
 | ||
|     if ((result[0] & 0x80) == 0)
 | ||
|     {
 | ||
|         DBG (DL_DATA_TRACE, "%s: sense key is invalid.\n", me);
 | ||
|         return SANE_STATUS_GOOD;    /* sense key invalid */
 | ||
|     } else {
 | ||
|         DBG (DL_DATA_TRACE, "%s: sense key: 0x%02x, asc: 0x%02x, ascq: 0x%02x, i1: 0x%02x, i2: 0x%02x\n",
 | ||
|         me, sense, asc, ascq, result[18], result[19]);
 | ||
|     }
 | ||
| 
 | ||
|     switch (sense)
 | ||
|     {
 | ||
|     case 0x00:
 | ||
|         /* no sense */
 | ||
|         sense_str = "No sense.";
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
 | ||
|         break;
 | ||
|     case 0x02:
 | ||
|         /* not ready */
 | ||
|         sense_str = "Not ready.";
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
 | ||
|         if (asc == 0x04  &&  ascq == 0x01)
 | ||
|         {
 | ||
|             /* warming up; byte 18 contains remaining seconds */
 | ||
|             as_str = "Logical unit is in process of becoming ready.";
 | ||
|             DBG (DL_MINOR_INFO, "%s: %s (%d seconds)\n", me, as_str, result[18]);
 | ||
|             status = SANE_STATUS_DEVICE_BUSY;
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
 | ||
|         }
 | ||
|         break;
 | ||
|     case 0x04:
 | ||
|         /* hardware error */
 | ||
|         sense_str = "Hardware error.";
 | ||
|         /* byte 18 and 19 detail the hardware problems */
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s (0x%02x, 0x%02x)\n", me, sense_str, result[18],
 | ||
|             result[19]);
 | ||
|         status = SANE_STATUS_IO_ERROR;
 | ||
|         break;
 | ||
|     case 0x05:
 | ||
|         /* illegal request */
 | ||
|         sense_str = "Illegal request.";
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
 | ||
|         if (asc == 0x25 && ascq == 0x00)
 | ||
|             as_str = "Logical unit not supported.";
 | ||
|             DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
 | ||
|         status = SANE_STATUS_IO_ERROR;
 | ||
|         break;
 | ||
|     case 0x09:
 | ||
|         /* process error */
 | ||
|         sense_str = "Process error.";
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
 | ||
|         if (asc == 0x00 && ascq == 0x05)
 | ||
|         {
 | ||
|             /* no documents in ADF */
 | ||
|             as_str = "End of data detected.";
 | ||
|             DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
 | ||
|             status = SANE_STATUS_NO_DOCS;
 | ||
|         }
 | ||
|         else if (asc == 0x3b && ascq == 0x05)
 | ||
|         {
 | ||
|             /* paper jam in ADF */
 | ||
|             as_str = "Paper jam.";
 | ||
|             DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
 | ||
|             status = SANE_STATUS_JAMMED;
 | ||
|         }
 | ||
|         else if (asc == 0x3b && ascq == 0x09)
 | ||
|         {
 | ||
|             /* scanning area exceeds end of paper in ADF */
 | ||
|             as_str = "Read past end of medium.";
 | ||
|             DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
 | ||
|             status = SANE_STATUS_EOF;
 | ||
|         }
 | ||
|         break;
 | ||
|     case 0x0b:
 | ||
|         /* Aborted command */
 | ||
|         sense_str = "Aborted Command.";
 | ||
|         DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
 | ||
|         status = SANE_STATUS_IO_ERROR;
 | ||
|         break;
 | ||
|     default:
 | ||
|         DBG (DL_MINOR_ERROR, "%s: no handling for sense %x.\n", me, sense);
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     if (pss)
 | ||
|     {
 | ||
|         pss->sense_str = sense_str;
 | ||
|         pss->as_str = as_str;
 | ||
|     }
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static SANE_Status open_scanner (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     SANE_Status status;
 | ||
|     DBG (DL_CALL_TRACE, "open_scanner\n");
 | ||
|     if (!pss->opens)
 | ||
|     {
 | ||
|         if(pss->pdev->bus == SCSI)
 | ||
|         {
 | ||
|             status = sanei_scsi_open (pss->devname, &(pss->fd),
 | ||
|                 sense_handler, (void *) pss);
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             status = snapscani_usb_open (pss->devname, &(pss->fd),
 | ||
|                 sense_handler, (void *) pss);
 | ||
|         }
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|         /* already open */
 | ||
|         status = SANE_STATUS_GOOD;
 | ||
|     }
 | ||
|     if (status == SANE_STATUS_GOOD)
 | ||
|         pss->opens++;
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static void close_scanner (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static char me[] = "close_scanner";
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     if (pss->opens)
 | ||
|     {
 | ||
|         pss->opens--;
 | ||
|         if (!pss->opens)
 | ||
|         {
 | ||
|             if(pss->pdev->bus == SCSI)
 | ||
|             {
 | ||
|                 sanei_scsi_close (pss->fd);
 | ||
|             }
 | ||
|             else if(pss->pdev->bus == USB)
 | ||
|             {
 | ||
|                 snapscani_usb_close (pss->fd);
 | ||
|             }
 | ||
|         } else {
 | ||
|             DBG(DL_INFO, "%s: handles left: %d\n,",me, pss->opens);
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status snapscan_cmd(SnapScan_Bus bus, int fd, const void *src,
 | ||
|                 size_t src_size, void *dst, size_t * dst_size)
 | ||
| {
 | ||
|     SANE_Status status;
 | ||
|     DBG (DL_CALL_TRACE, "snapscan_cmd\n");
 | ||
|     if(bus == USB)
 | ||
|     {
 | ||
|         status = snapscani_usb_cmd(fd, src, src_size, dst, dst_size);
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|         status = sanei_scsi_cmd(fd, src, src_size, dst, dst_size);
 | ||
|     }
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| /* SCSI commands */
 | ||
| #define TEST_UNIT_READY        0x00
 | ||
| #define INQUIRY                0x12
 | ||
| #define SEND                   0x2A
 | ||
| #define SET_WINDOW             0x24
 | ||
| #define SCAN                   0x1B
 | ||
| #define READ                   0x28
 | ||
| #define REQUEST_SENSE          0x03
 | ||
| #define RESERVE_UNIT           0x16
 | ||
| #define RELEASE_UNIT           0x17
 | ||
| #define SEND_DIAGNOSTIC        0x1D
 | ||
| #define OBJECT_POSITION        0x31
 | ||
| #define GET_DATA_BUFFER_STATUS 0x34
 | ||
| 
 | ||
| #define SCAN_LEN 6
 | ||
| #define READ_LEN 10
 | ||
| 
 | ||
| /*  buffer tools */
 | ||
| 
 | ||
| static void zero_buf (u_char * buf, size_t len)
 | ||
| {
 | ||
|     size_t i;
 | ||
| 
 | ||
|     for (i = 0;  i < len;  i++)
 | ||
|         buf[i] = 0x00;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| #define BYTE_SIZE 8
 | ||
| 
 | ||
| static u_short u_char_to_u_short (u_char * pc)
 | ||
| {
 | ||
|     u_short r = 0;
 | ||
|     r |= pc[0];
 | ||
|     r = r << BYTE_SIZE;
 | ||
|     r |= pc[1];
 | ||
|     return r;
 | ||
| }
 | ||
| 
 | ||
| static void u_short_to_u_charp (u_short x, u_char * pc)
 | ||
| {
 | ||
|     pc[0] = (0xFF00 & x) >> BYTE_SIZE;
 | ||
|     pc[1] = (0x00FF & x);
 | ||
| }
 | ||
| 
 | ||
| static void u_int_to_u_char3p (u_int x, u_char * pc)
 | ||
| {
 | ||
|     pc[0] = (0xFF0000 & x) >> 2 * BYTE_SIZE;
 | ||
|     pc[1] = (0x00FF00 & x) >> BYTE_SIZE;
 | ||
|     pc[2] = (0x0000FF & x);
 | ||
| }
 | ||
| 
 | ||
| static void u_int_to_u_char4p (u_int x, u_char * pc)
 | ||
| {
 | ||
|     pc[0] = (0xFF000000 & x) >> 3 * BYTE_SIZE;
 | ||
|     pc[1] = (0x00FF0000 & x) >> 2 * BYTE_SIZE;
 | ||
|     pc[2] = (0x0000FF00 & x) >> BYTE_SIZE;
 | ||
|     pc[3] = (0x000000FF & x);
 | ||
| }
 | ||
| 
 | ||
| /* Convert 'STRING   ' to 'STRING' by adding 0 after last non-space */
 | ||
| static void remove_trailing_space (char *s)
 | ||
| {
 | ||
|     int position;
 | ||
| 
 | ||
|     if (NULL == s)
 | ||
|         return;
 | ||
| 
 | ||
|     for (position = strlen (s);
 | ||
|          position > 0  &&  ' ' == s[position - 1];
 | ||
|          position--)
 | ||
|     {
 | ||
|         /* dummy */;
 | ||
|     }
 | ||
|     s[position] = 0;
 | ||
| }
 | ||
| 
 | ||
| static void check_range (int *v, SANE_Range r)
 | ||
| {
 | ||
|     if (*v < r.min)
 | ||
|     *v = r.min;
 | ||
|     if (*v > r.max)
 | ||
|     *v = r.max;
 | ||
| }
 | ||
| 
 | ||
| #define INQUIRY_LEN 6
 | ||
| #define INQUIRY_RET_LEN 120
 | ||
| #define INQUIRY_RET_LEN_EPSON 139
 | ||
| #define INQUIRY_RET_LEN_5000  138
 | ||
| 
 | ||
| #define INQUIRY_VENDOR         8    /* Offset in reply data to vendor name */
 | ||
| #define INQUIRY_PRODUCT       16    /* Offset in reply data to product id */
 | ||
| #define INQUIRY_REV           32    /* Offset in reply data to revision level */
 | ||
| #define INQUIRY_PRL2          36    /* Product Revision Level 2 (AGFA) */
 | ||
| #define INQUIRY_HCFG          37    /* Hardware Configuration (AGFA) */
 | ||
| #define INQUIRY_HWST          40    /* Hardware status */
 | ||
| #define INQUIRY_HWMI          41    /* HARDWARE Model ID */
 | ||
| #define INQUIRY_PIX_PER_LINE  42    /* Pixels per scan line (AGFA) */
 | ||
| #define INQUIRY_BYTE_PER_LINE 44    /* Bytes per scan line (AGFA) */
 | ||
| #define INQUIRY_NUM_LINES     46    /* number of scan lines (AGFA) */
 | ||
| #define INQUIRY_OPT_RES       48    /* optical resolution (AGFA) */
 | ||
| #define INQUIRY_SCAN_SPEED    51    /* scan speed (AGFA) */
 | ||
| #define INQUIRY_EXPTIME1      52    /* exposure time, first digit (AGFA) */
 | ||
| #define INQUIRY_EXPTIME2      53    /* exposure time, second digit (AGFA) */
 | ||
| #define INQUIRY_G2R_DIFF      54    /* green to red difference (AGFA) */
 | ||
| #define INQUIRY_B2R_DIFF      55    /* green to red difference (AGFA) */
 | ||
| #define INQUIRY_FIRMWARE      96    /* firmware date and time (AGFA) */
 | ||
| #define INQUIRY_BYTE_PER_LINE_MSB    132   /* ?? top byte of bytes per scan line - epson 2480 */
 | ||
| #define INQUIRY_EPSON_HCFG   138    /* ?? Hardware configuration (Epson) */
 | ||
| 
 | ||
| 
 | ||
| /* a mini-inquiry reads only the first 36 bytes of inquiry data, and
 | ||
|    returns the vendor(7 chars) and model(16 chars); vendor and model
 | ||
|    must point to character buffers of size at least 8 and 17
 | ||
|    respectively */
 | ||
| 
 | ||
| static SANE_Status mini_inquiry (SnapScan_Bus bus, int fd, char *vendor, char *model)
 | ||
| {
 | ||
|     static const char *me = "mini_inquiry";
 | ||
|     size_t read_bytes;
 | ||
|     char cmd[] = {INQUIRY, 0, 0, 0, 36, 0};
 | ||
|     char data[36];
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     read_bytes = 36;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = snapscan_cmd (bus, fd, cmd, sizeof (cmd), data, &read_bytes);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
| 
 | ||
|     memcpy (vendor, data + 8, 7);
 | ||
|     vendor[7] = 0;
 | ||
|     memcpy (model, data + 16, 16);
 | ||
|     model[16] = 0;
 | ||
| 
 | ||
|     remove_trailing_space (vendor);
 | ||
|     remove_trailing_space (model);
 | ||
| 
 | ||
|     return SANE_STATUS_GOOD;
 | ||
| }
 | ||
| 
 | ||
| /* TODO: Remove */
 | ||
| static char *snapscani_debug_data(char *str,const SANE_Byte *data, int len) {
 | ||
|     char tmpstr[10];
 | ||
|     int i;
 | ||
| 
 | ||
|     str[0]=0;
 | ||
|     for(i=0; i < (len < 20 ? len : 20); i++) {
 | ||
|         sprintf(tmpstr," 0x%02x",((int)data[i]) & 0xff);
 | ||
|         if(i%16 == 0 && i != 0)
 | ||
|             strcat(str,"\n");
 | ||
|         strcat(str,tmpstr);
 | ||
|     }
 | ||
|     if(i < len)
 | ||
|         strcat(str," ...");
 | ||
|     return str;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status inquiry (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "inquiry";
 | ||
|     char tmpstr[150]; /* TODO: Remove */
 | ||
|     SANE_Status status;
 | ||
|     switch (pss->pdev->model)
 | ||
|     {
 | ||
|     case PERFECTION2480:
 | ||
|     case PERFECTION3490:
 | ||
|         if (pss->firmware_loaded)
 | ||
|           pss->read_bytes = INQUIRY_RET_LEN_EPSON;
 | ||
|         else
 | ||
|           pss->read_bytes = INQUIRY_RET_LEN;
 | ||
|         break;
 | ||
|     case PRISA5000:
 | ||
|     case PRISA5150:
 | ||
|         pss->read_bytes = INQUIRY_RET_LEN_5000;
 | ||
|         break;
 | ||
|     default:
 | ||
|         pss->read_bytes = INQUIRY_RET_LEN;
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|     pss->cmd[0] = INQUIRY;
 | ||
|     pss->cmd[4] = pss->read_bytes;
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = snapscan_cmd (pss->pdev->bus,
 | ||
|                pss->fd,
 | ||
|                pss->cmd,
 | ||
|                INQUIRY_LEN,
 | ||
|                pss->buf,
 | ||
|                &pss->read_bytes);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
| 
 | ||
|     /* record current parameters */
 | ||
|     {
 | ||
|         char exptime[4] = {' ', '.', ' ', 0};
 | ||
|         exptime[0] = (char) (pss->buf[INQUIRY_EXPTIME1] + '0');
 | ||
|         exptime[2] = (char) (pss->buf[INQUIRY_EXPTIME2] + '0');
 | ||
|         pss->ms_per_line = atof (exptime)*(float) pss->buf[INQUIRY_SCAN_SPEED];
 | ||
|         DBG (DL_DATA_TRACE, "%s: exposure time: %s ms\n", me, exptime);
 | ||
|         DBG (DL_DATA_TRACE, "%s: ms per line: %f\n", me, pss->ms_per_line);
 | ||
|     }
 | ||
| 
 | ||
|     switch (pss->pdev->model)
 | ||
|     {
 | ||
|     case SNAPSCAN:
 | ||
|     case ACER300F:
 | ||
|         pss->chroma_offset[R_CHAN] =
 | ||
|         pss->chroma_offset[G_CHAN] =
 | ||
|         pss->chroma_offset[B_CHAN] = 0;
 | ||
|         pss->chroma = 0;
 | ||
|         break;
 | ||
|     case PERFECTION2480:
 | ||
|     case PERFECTION3490:
 | ||
|         if (pss->firmware_loaded)
 | ||
|         {
 | ||
|             snapscani_debug_data(tmpstr, pss->buf+120, 19);
 | ||
|             DBG (DL_DATA_TRACE, "%s: Epson additional inquiry data:\n%s\n", me, tmpstr);
 | ||
|             pss->hconfig_epson = pss->buf[INQUIRY_EPSON_HCFG];
 | ||
|         }
 | ||
|         /* fall through */
 | ||
|     default:
 | ||
|     {
 | ||
|         signed char min_diff;
 | ||
|         u_char r_off, g_off, b_off;
 | ||
|         signed char g = (pss->buf[INQUIRY_G2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_G2R_DIFF] & 0x7F) : pss->buf[INQUIRY_G2R_DIFF];
 | ||
|         signed char b = (pss->buf[INQUIRY_B2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_B2R_DIFF] & 0x7F) : pss->buf[INQUIRY_B2R_DIFF];
 | ||
|         DBG (DL_DATA_TRACE, "%s: G2R_DIFF: %d\n", me, pss->buf[INQUIRY_G2R_DIFF]);
 | ||
|         DBG (DL_DATA_TRACE, "%s: B2R_DIFF: %d\n", me, pss->buf[INQUIRY_B2R_DIFF]);
 | ||
| 
 | ||
|         min_diff = MIN (MIN (b, g), 0);
 | ||
|         r_off = (u_char) (0 - min_diff);
 | ||
|         g_off = (u_char) (g - min_diff);
 | ||
|         b_off = (u_char) (b - min_diff);
 | ||
|         pss->chroma_offset[R_CHAN] = r_off;
 | ||
|         pss->chroma_offset[G_CHAN] = g_off;
 | ||
|         pss->chroma_offset[B_CHAN] = b_off;
 | ||
|         pss->chroma = MAX(MAX(r_off, g_off), b_off);
 | ||
|         DBG (DL_DATA_TRACE,
 | ||
|             "%s: Chroma offsets=%d; Red=%u, Green:=%u, Blue=%u\n",
 | ||
|             me, pss->chroma,
 | ||
|             pss->chroma_offset[R_CHAN],
 | ||
|             pss->chroma_offset[G_CHAN],
 | ||
|             pss->chroma_offset[B_CHAN]);
 | ||
|         }
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     pss->actual_res =
 | ||
|         u_char_to_u_short (pss->buf + INQUIRY_OPT_RES);
 | ||
| 
 | ||
|     pss->pixels_per_line =
 | ||
|         u_char_to_u_short (pss->buf + INQUIRY_PIX_PER_LINE);
 | ||
|     pss->bytes_per_line =
 | ||
|         u_char_to_u_short (pss->buf + INQUIRY_BYTE_PER_LINE);
 | ||
|     if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
 | ||
|         pss->bytes_per_line += pss->buf[INQUIRY_BYTE_PER_LINE_MSB] << 16;
 | ||
|     pss->lines =
 | ||
|         u_char_to_u_short (pss->buf + INQUIRY_NUM_LINES) - pss->chroma;
 | ||
|     /* effective buffer size must be a whole number of scan lines */
 | ||
|     if (pss->lines)
 | ||
|         pss->buf_sz = (pss->phys_buf_sz/pss->bytes_per_line)*pss->bytes_per_line;
 | ||
|     else
 | ||
|         pss->buf_sz = 0;
 | ||
|     pss->bytes_remaining = pss->bytes_per_line * (pss->lines + pss->chroma);
 | ||
|     pss->expected_read_bytes = 0;
 | ||
|     pss->read_bytes = 0;
 | ||
|     pss->hwst = pss->buf[INQUIRY_HWST];
 | ||
|     if ((pss->pdev->bus == USB) && !(pss->hwst & 0x02))
 | ||
|     {
 | ||
|         pss->firmware_loaded = SANE_TRUE;
 | ||
|     }
 | ||
|     pss->hconfig = pss->buf[INQUIRY_HCFG];
 | ||
|     switch (pss->pdev->model)
 | ||
|     {
 | ||
|     case PERFECTION1270:
 | ||
|     case PERFECTION1670:
 | ||
|     case PERFECTION2480:
 | ||
|     case PERFECTION3490:
 | ||
|     case PRISA5150:
 | ||
|     case PRISA5000:
 | ||
|         pss->bpp = 14;
 | ||
|         break;
 | ||
|     case STYLUS_CX1500:
 | ||
|     case SCANWIT2720S:
 | ||
|         pss->bpp = 12;
 | ||
|         break;
 | ||
|     default:
 | ||
|         pss->bpp = 8;
 | ||
|         if (pss->hconfig & HCFG_ADC)
 | ||
|             pss->bpp = 10;
 | ||
|         break;
 | ||
|     }
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|         "%s: hardware config = 0x%02x\n",
 | ||
|         me,
 | ||
|         pss->hconfig);
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|         "%s: bits per pixel = %lu\n",
 | ||
|         me,
 | ||
|         (u_long) pss->bpp);
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|         "%s: pixels per scan line = %lu\n",
 | ||
|         me,
 | ||
|         (u_long) pss->pixels_per_line);
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|         "%s: bytes per scan line = %lu\n",
 | ||
|         me,
 | ||
|         (u_long) pss->bytes_per_line);
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|         "%s: number of scan lines = %lu\n",
 | ||
|         me,
 | ||
|         (u_long) pss->lines);
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|          "%s: effective buffer size = %lu bytes, %lu lines\n",
 | ||
|          me,
 | ||
|         (u_long) pss->buf_sz,
 | ||
|         (u_long) (pss->lines ? pss->buf_sz / pss->lines : 0));
 | ||
|     DBG (DL_DATA_TRACE,
 | ||
|         "%s: expected total scan data: %lu bytes\n",
 | ||
|         me,
 | ||
|         (u_long) pss->bytes_remaining);
 | ||
| 
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status test_unit_ready (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "test_unit_ready";
 | ||
|     char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static void reserve_unit (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "reserve_unit";
 | ||
|     char cmd[] = {RESERVE_UNIT, 0, 0, 0, 0, 0};
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
 | ||
|     if (status != SANE_STATUS_GOOD)
 | ||
|     {
 | ||
|         DBG (DL_MAJOR_ERROR,
 | ||
|             "%s: scsi command error: %s\n",
 | ||
|             me,
 | ||
|             sane_strstatus (status));
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void release_unit (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "release_unit";
 | ||
|     char cmd[] = {RELEASE_UNIT, 0, 0, 0, 0, 0};
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
 | ||
|     if (status != SANE_STATUS_GOOD)
 | ||
|     {
 | ||
|         DBG (DL_MAJOR_ERROR,
 | ||
|             "%s: scsi command error: %s\n",
 | ||
|             me, sane_strstatus (status));
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| #define SEND_LENGTH 10
 | ||
| #define DTC_HALFTONE 0x02
 | ||
| #define DTC_GAMMA 0x03
 | ||
| #define DTC_GAMMA2 0x04
 | ||
| #define DTC_SPEED 0x81
 | ||
| #define DTC_CALIBRATION 0x82
 | ||
| #define DTC_CALIBRATION_BLACK 0x89
 | ||
| #define DTCQ_HALFTONE_BW8 0x00
 | ||
| #define DTCQ_HALFTONE_COLOR8 0x01
 | ||
| #define DTCQ_HALFTONE_BW16 0x80
 | ||
| #define DTCQ_HALFTONE_COLOR16 0x81
 | ||
| #define DTCQ_GAMMA_GRAY8 0x00
 | ||
| #define DTCQ_GAMMA_RED8 0x01
 | ||
| #define DTCQ_GAMMA_GREEN8 0x02
 | ||
| #define DTCQ_GAMMA_BLUE8 0x03
 | ||
| #define DTCQ_GAMMA_GRAY10 0x80
 | ||
| #define DTCQ_GAMMA_RED10 0x81
 | ||
| #define DTCQ_GAMMA_GREEN10 0x82
 | ||
| #define DTCQ_GAMMA_BLUE10 0x83
 | ||
| #define DTCQ_GAMMA_GRAY12 0x90
 | ||
| #define DTCQ_GAMMA_RED12 0x91
 | ||
| #define DTCQ_GAMMA_GREEN12 0x92
 | ||
| #define DTCQ_GAMMA_BLUE12 0x93
 | ||
| #define DTCQ_GAMMA_GRAY14 0x95 /* ? */
 | ||
| #define DTCQ_GAMMA_RED14 0x96
 | ||
| #define DTCQ_GAMMA_GREEN14 0x97
 | ||
| #define DTCQ_GAMMA_BLUE14 0x98
 | ||
| #define DTCQ_GAMMA_GRAY12_16BIT 0xa0
 | ||
| #define DTCQ_GAMMA_RED12_16BIT 0xa1
 | ||
| #define DTCQ_GAMMA_GREEN12_16BIT 0xa2
 | ||
| #define DTCQ_GAMMA_BLUE12_16BIT 0xa3
 | ||
| #define DTCQ_GAMMA_GRAY14_16BIT 0xa5 /* ? */
 | ||
| #define DTCQ_GAMMA_RED14_16BIT 0xa6
 | ||
| #define DTCQ_GAMMA_GREEN14_16BIT 0xa7
 | ||
| #define DTCQ_GAMMA_BLUE14_16BIT 0xa8
 | ||
| 
 | ||
| static SANE_Status send (SnapScan_Scanner *pss, u_char dtc, u_char dtcq)
 | ||
| {
 | ||
|     static char me[] = "send";
 | ||
|     SANE_Status status;
 | ||
|     u_short tl;            /* transfer length */
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
| 
 | ||
|     zero_buf (pss->buf, SEND_LENGTH);
 | ||
| 
 | ||
|     switch (dtc)
 | ||
|     {
 | ||
|     case DTC_HALFTONE:        /* halftone mask */
 | ||
|         switch (dtcq)
 | ||
|         {
 | ||
|         case DTCQ_HALFTONE_BW8:
 | ||
|             tl = 64;        /* bw 8x8 table */
 | ||
|             break;
 | ||
|         case DTCQ_HALFTONE_COLOR8:
 | ||
|             tl = 3 * 64;    /* rgb 8x8 tables */
 | ||
|             break;
 | ||
|         case DTCQ_HALFTONE_BW16:
 | ||
|             tl = 256;        /* bw 16x16 table */
 | ||
|             break;
 | ||
|         case DTCQ_HALFTONE_COLOR16:
 | ||
|             tl = 3 * 256;    /* rgb 16x16 tables */
 | ||
|             break;
 | ||
|         default:
 | ||
|             DBG (DL_MAJOR_ERROR, "%s: bad halftone data type qualifier 0x%x\n",
 | ||
|                  me, dtcq);
 | ||
|             return SANE_STATUS_INVAL;
 | ||
|         }
 | ||
|         break;
 | ||
|     case DTC_GAMMA:        /* gamma function */
 | ||
|     case DTC_GAMMA2:
 | ||
|         switch (dtcq)
 | ||
|         {
 | ||
|         case DTCQ_GAMMA_GRAY8:    /* 8-bit tables */
 | ||
|         case DTCQ_GAMMA_RED8:
 | ||
|         case DTCQ_GAMMA_GREEN8:
 | ||
|         case DTCQ_GAMMA_BLUE8:
 | ||
|             tl = 256;
 | ||
|             break;
 | ||
|         case DTCQ_GAMMA_GRAY10:    /* 10-bit tables */
 | ||
|         case DTCQ_GAMMA_RED10:
 | ||
|         case DTCQ_GAMMA_GREEN10:
 | ||
|         case DTCQ_GAMMA_BLUE10:
 | ||
|             tl = 1024;
 | ||
|             break;
 | ||
|         case DTCQ_GAMMA_GRAY12:    /* 12-bit tables */
 | ||
|         case DTCQ_GAMMA_RED12:
 | ||
|         case DTCQ_GAMMA_GREEN12:
 | ||
|         case DTCQ_GAMMA_BLUE12:
 | ||
|             tl = 4096;
 | ||
|             break;
 | ||
|         case DTCQ_GAMMA_GRAY12_16BIT:    /* 12-bit tables with 16 bit data */
 | ||
|         case DTCQ_GAMMA_RED12_16BIT:
 | ||
|         case DTCQ_GAMMA_GREEN12_16BIT:
 | ||
|         case DTCQ_GAMMA_BLUE12_16BIT:
 | ||
|             tl = 8192;
 | ||
|             break;
 | ||
|         case DTCQ_GAMMA_GRAY14:    /* 14-bit tables */
 | ||
|         case DTCQ_GAMMA_RED14:
 | ||
|         case DTCQ_GAMMA_GREEN14:
 | ||
|         case DTCQ_GAMMA_BLUE14:
 | ||
|             tl = 16384;
 | ||
|             break;
 | ||
|         case DTCQ_GAMMA_GRAY14_16BIT:    /* 14-bit tables with 16 bit data */
 | ||
|         case DTCQ_GAMMA_RED14_16BIT:
 | ||
|         case DTCQ_GAMMA_GREEN14_16BIT:
 | ||
|         case DTCQ_GAMMA_BLUE14_16BIT:
 | ||
|             tl = 32768;
 | ||
|             break;
 | ||
|         default:
 | ||
|             DBG (DL_MAJOR_ERROR, "%s: bad gamma data type qualifier 0x%x\n",
 | ||
|                  me, dtcq);
 | ||
|             return SANE_STATUS_INVAL;
 | ||
|         }
 | ||
|         break;
 | ||
|     case DTC_SPEED:        /* static transfer speed */
 | ||
|         tl = 2;
 | ||
|         break;
 | ||
|     case DTC_CALIBRATION:
 | ||
|         tl = calibration_line_length(pss);
 | ||
|         break;
 | ||
|     default:
 | ||
|         DBG (DL_MAJOR_ERROR, "%s: unsupported data type code 0x%x\n",
 | ||
|              me, (unsigned) dtc);
 | ||
|         return SANE_STATUS_INVAL;
 | ||
|     }
 | ||
| 
 | ||
|     pss->buf[0] = SEND;
 | ||
|     pss->buf[2] = dtc;
 | ||
|     pss->buf[5] = dtcq;
 | ||
|     pss->buf[7] = (tl >> 8) & 0xff;
 | ||
|     pss->buf[8] = tl & 0xff;
 | ||
| 
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
 | ||
|                              NULL, NULL);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| #include "snapscan-data.c"
 | ||
| static SANE_Status send_calibration_5150(SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const int length = sizeof(calibration_data_5150);
 | ||
|     SANE_Byte* buf = malloc (length + SEND_LENGTH);
 | ||
|     SANE_Status status;
 | ||
|     zero_buf (buf, SEND_LENGTH);
 | ||
|     *buf = SEND;
 | ||
|     *(buf + 2) = DTC_CALIBRATION;
 | ||
|     *(buf + 6) = (length >> 16) & 0xff;
 | ||
|     *(buf + 7) = (length >> 8) & 0xff;
 | ||
|     *(buf + 8) = length & 0xff;
 | ||
|     memcpy(buf + SEND_LENGTH, calibration_data_5150, length);
 | ||
|     status = snapscan_cmd (
 | ||
|       pss->pdev->bus, pss->fd, buf, SEND_LENGTH + length, NULL, NULL);
 | ||
|     free (buf);
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| #define SET_WINDOW_LEN             10
 | ||
| #define SET_WINDOW_HEADER         10    /* header starts */
 | ||
| #define SET_WINDOW_HEADER_LEN         8
 | ||
| #define SET_WINDOW_DESC         18    /* window descriptor starts */
 | ||
| #define SET_WINDOW_DESC_LEN         48
 | ||
| #define SET_WINDOW_TRANSFER_LEN     56
 | ||
| #define SET_WINDOW_TOTAL_LEN         66
 | ||
| #define SET_WINDOW_RET_LEN           0    /* no returned data */
 | ||
| 
 | ||
| #define SET_WINDOW_P_TRANSFER_LEN     6
 | ||
| #define SET_WINDOW_P_DESC_LEN         6
 | ||
| 
 | ||
| #define SET_WINDOW_P_WIN_ID         0
 | ||
| #define SET_WINDOW_P_XRES           2
 | ||
| #define SET_WINDOW_P_YRES           4
 | ||
| #define SET_WINDOW_P_TLX            6
 | ||
| #define SET_WINDOW_P_TLY            10
 | ||
| #define SET_WINDOW_P_WIDTH          14
 | ||
| #define SET_WINDOW_P_LENGTH         18
 | ||
| #define SET_WINDOW_P_BRIGHTNESS       22
 | ||
| #define SET_WINDOW_P_THRESHOLD        23
 | ||
| #define SET_WINDOW_P_CONTRAST         24
 | ||
| #define SET_WINDOW_P_COMPOSITION      25
 | ||
| #define SET_WINDOW_P_BITS_PER_PIX     26
 | ||
| #define SET_WINDOW_P_HALFTONE_PATTERN      27
 | ||
| #define SET_WINDOW_P_PADDING_TYPE          29
 | ||
| #define SET_WINDOW_P_BIT_ORDERING          30
 | ||
| #define SET_WINDOW_P_COMPRESSION_TYPE      32
 | ||
| #define SET_WINDOW_P_COMPRESSION_ARG       33
 | ||
| #define SET_WINDOW_P_HALFTONE_FLAG         35
 | ||
| #define SET_WINDOW_P_DEBUG_MODE            40
 | ||
| #define SET_WINDOW_P_GAMMA_NO              41
 | ||
| #define SET_WINDOW_P_OPERATION_MODE        42
 | ||
| #define SET_WINDOW_P_RED_UNDER_COLOR       43
 | ||
| #define SET_WINDOW_P_BLUE_UNDER_COLOR      45
 | ||
| #define SET_WINDOW_P_GREEN_UNDER_COLOR     44
 | ||
| 
 | ||
| static SANE_Status prepare_set_window (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "prepare_set_window";
 | ||
|     u_char *pc;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
| 
 | ||
|     /* basic command */
 | ||
|     pc = pss->cmd;
 | ||
|     pc[0] = SET_WINDOW;
 | ||
|     u_int_to_u_char3p ((u_int) SET_WINDOW_TRANSFER_LEN,
 | ||
|                        pc + SET_WINDOW_P_TRANSFER_LEN);
 | ||
| 
 | ||
|     /* header; we support only one window */
 | ||
|     pc += SET_WINDOW_LEN;
 | ||
|     u_short_to_u_charp (SET_WINDOW_DESC_LEN, pc + SET_WINDOW_P_DESC_LEN);
 | ||
| 
 | ||
|     /* the sole window descriptor */
 | ||
|     pc += SET_WINDOW_HEADER_LEN;
 | ||
|     pc[SET_WINDOW_P_WIN_ID] = 0;
 | ||
|     u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_XRES);
 | ||
|     u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_YRES);
 | ||
|     DBG (DL_CALL_TRACE, "%s Resolution: %d\n", me, pss->res);
 | ||
| 
 | ||
|     pc[SET_WINDOW_P_BRIGHTNESS] = 128;
 | ||
|     pc[SET_WINDOW_P_THRESHOLD] =
 | ||
|         (u_char) (255.0*(pss->threshold / 100.0));
 | ||
|     pc[SET_WINDOW_P_CONTRAST] = 128;
 | ||
| 
 | ||
|     {
 | ||
|         SnapScan_Mode mode = pss->mode;
 | ||
|         pss->bpp_scan = pss->val[OPT_BIT_DEPTH].w;
 | ||
| 
 | ||
|         if (pss->preview)
 | ||
|         {
 | ||
|             mode = pss->preview_mode;
 | ||
|             if (pss->pdev->model != SCANWIT2720S)
 | ||
|                 pss->bpp_scan = 8;
 | ||
|         }
 | ||
| 
 | ||
|         DBG (DL_MINOR_INFO, "%s Mode: %d\n", me, mode);
 | ||
|         switch (mode)
 | ||
|         {
 | ||
|         case MD_COLOUR:
 | ||
|             pc[SET_WINDOW_P_COMPOSITION] = 0x05;    /* multi-level RGB */
 | ||
|             break;
 | ||
|         case MD_BILEVELCOLOUR:
 | ||
|             if (pss->halftone)
 | ||
|                 pc[SET_WINDOW_P_COMPOSITION] = 0x04;    /* halftone RGB */
 | ||
|             else
 | ||
|                 pc[SET_WINDOW_P_COMPOSITION] = 0x03;    /* bi-level RGB */
 | ||
|             pss->bpp_scan = 1;
 | ||
|             break;
 | ||
|         case MD_GREYSCALE:
 | ||
|             pc[SET_WINDOW_P_COMPOSITION] = 0x02;    /* grayscale */
 | ||
|             break;
 | ||
|         case MD_LINEART:
 | ||
|             if (pss->halftone)
 | ||
|                 pc[SET_WINDOW_P_COMPOSITION] = 0x01;    /* b&w halftone */
 | ||
|             else
 | ||
|                 pc[SET_WINDOW_P_COMPOSITION] = 0x00;    /* b&w (lineart) */
 | ||
|             pss->bpp_scan = 1;
 | ||
|             break;
 | ||
|         default:
 | ||
|             break;
 | ||
|         }
 | ||
| 
 | ||
|         pc[SET_WINDOW_P_BITS_PER_PIX] = pss->bpp_scan;
 | ||
|         DBG (DL_INFO, "%s: bits-per-pixel set to %d\n", me, (int) pss->bpp_scan);
 | ||
|     }
 | ||
|     /* the RIF bit is the high bit of the padding type */
 | ||
|     pc[SET_WINDOW_P_PADDING_TYPE] = 0x00 /*| (pss->negative ? 0x00 : 0x80) */ ;
 | ||
|     pc[SET_WINDOW_P_HALFTONE_PATTERN] = 0;
 | ||
|     pc[SET_WINDOW_P_HALFTONE_FLAG] = 0x80;    /* always set; image
 | ||
|                                             composition
 | ||
|                                             determines whether
 | ||
|                                             halftone is
 | ||
|                                             actually used */
 | ||
| 
 | ||
|     u_short_to_u_charp (0x0000, pc + SET_WINDOW_P_BIT_ORDERING);    /* used? */
 | ||
|     pc[SET_WINDOW_P_COMPRESSION_TYPE] = 0;    /* none */
 | ||
|     pc[SET_WINDOW_P_COMPRESSION_ARG] = 0;    /* none applicable */
 | ||
|     if(pss->pdev->model != ACER300F
 | ||
|        &&
 | ||
|        pss->pdev->model != SNAPSCAN310
 | ||
|        &&
 | ||
|        pss->pdev->model != PRISA310
 | ||
|        &&
 | ||
|        pss->pdev->model != PRISA610
 | ||
|     ) {
 | ||
|         pc[SET_WINDOW_P_DEBUG_MODE] = 2;        /* use full 128k buffer */
 | ||
|         if (pss->mode != MD_LINEART)
 | ||
|         {
 | ||
|             pc[SET_WINDOW_P_GAMMA_NO] = 0x01;   /* downloaded gamma table */
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     pc[SET_WINDOW_P_RED_UNDER_COLOR] = 0xff;    /* defaults */
 | ||
|     pc[SET_WINDOW_P_BLUE_UNDER_COLOR] = 0xff;
 | ||
|     pc[SET_WINDOW_P_GREEN_UNDER_COLOR] = 0xff;
 | ||
| 
 | ||
|     return SANE_STATUS_GOOD;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status set_window (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "set_window";
 | ||
|     SANE_Status status;
 | ||
|     unsigned char source;
 | ||
|     u_char *pc;
 | ||
|     int pos_factor;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = prepare_set_window(pss);
 | ||
|     CHECK_STATUS (status, me, "prepare_set_window");
 | ||
| 
 | ||
|     pc = pss->cmd;
 | ||
| 
 | ||
|     /* header; we support only one window */
 | ||
|     pc += SET_WINDOW_LEN;
 | ||
| 
 | ||
|     /* the sole window descriptor */
 | ||
|     pc += SET_WINDOW_HEADER_LEN;
 | ||
| 
 | ||
|     switch (pss->pdev->model)
 | ||
|     {
 | ||
|         case PRISA5000:
 | ||
|         case PRISA5000E:
 | ||
|         case PRISA5150:
 | ||
|             pos_factor = (pss->res > 600) ?  1200 : 600;
 | ||
|             break;
 | ||
|         case PERFECTION1270:
 | ||
|         case PERFECTION1670:
 | ||
|             pos_factor = (pss->res > 800) ?  1600 : 800;
 | ||
|             break;
 | ||
|         case PERFECTION2480:
 | ||
|             pos_factor = (pss->res > 1200) ?  2400 : 1200;
 | ||
|             break;
 | ||
|         case PERFECTION3490:
 | ||
|             pos_factor = (pss->res > 1600) ?  3200 : 1600;
 | ||
|             break;
 | ||
|         default:
 | ||
|             pos_factor = pss->actual_res;
 | ||
|             break;
 | ||
|     }
 | ||
|     /* it's an ugly sound if the scanner drives against the rear
 | ||
|        wall, and with changing max values we better be sure */
 | ||
|     check_range(&(pss->brx), pss->pdev->x_range);
 | ||
|     check_range(&(pss->bry), pss->pdev->y_range);
 | ||
|     {
 | ||
|         int tlxp =
 | ||
|             (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tlx));
 | ||
|         int tlyp =
 | ||
|             (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tly));
 | ||
|         int brxp =
 | ||
|             (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->brx));
 | ||
|         int bryp =
 | ||
|             (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->bry));
 | ||
| 
 | ||
|         /* Check for brx > tlx and bry > tly */
 | ||
|         if (brxp <= tlxp) {
 | ||
|             tlxp = MAX (0, brxp - 75);
 | ||
|         }
 | ||
|         if (bryp <= tlyp) {
 | ||
|             tlyp = MAX (0, bryp - 75);
 | ||
|         }
 | ||
| 
 | ||
|         u_int_to_u_char4p (tlxp, pc + SET_WINDOW_P_TLX);
 | ||
|         u_int_to_u_char4p (tlyp, pc + SET_WINDOW_P_TLY);
 | ||
|         u_int_to_u_char4p (MAX (((unsigned) (brxp - tlxp)), 75),
 | ||
|                            pc + SET_WINDOW_P_WIDTH);
 | ||
|         u_int_to_u_char4p (MAX (((unsigned) (bryp - tlyp)), 75),
 | ||
|                            pc + SET_WINDOW_P_LENGTH);
 | ||
|         DBG (DL_INFO, "%s Width:  %d\n", me, brxp-tlxp);
 | ||
|         DBG (DL_INFO, "%s Length: %d\n", me, bryp-tlyp);
 | ||
|     }
 | ||
| 
 | ||
|     source = 0x0;
 | ||
|     if (pss->preview) {
 | ||
|         source |= 0x80; /* no high quality in preview */
 | ||
|     }
 | ||
|     else {
 | ||
|         source |= 0x40; /* no preview */
 | ||
|     }
 | ||
|     if (!pss->highquality) {
 | ||
|         source |= 0x80; /* no high quality */
 | ||
|     }
 | ||
|     if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490)) {
 | ||
|         source |= 0x40; /* 2480/3490 always need no_preview bit */
 | ||
|     }
 | ||
|     if (pss->source == SRC_TPO) {
 | ||
|         source |= 0x08;
 | ||
|     }
 | ||
|     if (pss->source == SRC_ADF) {
 | ||
|         source |= 0x10;
 | ||
|     }
 | ||
|     pc[SET_WINDOW_P_OPERATION_MODE] = source;
 | ||
|     DBG (DL_MINOR_INFO, "%s: operation mode set to 0x%02x\n", me, (int) source);
 | ||
| 
 | ||
|     do {
 | ||
|         status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
 | ||
|                   SET_WINDOW_TOTAL_LEN, NULL, NULL);
 | ||
|         if (status == SANE_STATUS_DEVICE_BUSY) {
 | ||
|             DBG (DL_MINOR_INFO, "%s: waiting for scanner to warm up\n", me);
 | ||
|             wait_scanner_ready (pss);
 | ||
|         }
 | ||
|     } while (status == SANE_STATUS_DEVICE_BUSY);
 | ||
| 
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status set_window_autofocus (SnapScan_Scanner *copy)
 | ||
| {
 | ||
|     static char me[] = "set_window_autofocus";
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s(%p)\n", me, (void*)copy);
 | ||
| 
 | ||
|     copy->res = copy->actual_res;
 | ||
|     status = prepare_set_window (copy);
 | ||
|     CHECK_STATUS (status, me, "prepare_set_window");
 | ||
| 
 | ||
|     u_int_to_u_char4p (1700, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_TLY);
 | ||
|     /* fill in width & height */
 | ||
|     u_int_to_u_char4p (2550, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_WIDTH);
 | ||
|     u_int_to_u_char4p (128, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_LENGTH);
 | ||
| 
 | ||
|     copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_BITS_PER_PIX] = 12;
 | ||
|     copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_OPERATION_MODE] = 0x49; /* focusing mode */
 | ||
|     return snapscan_cmd (copy->pdev->bus, copy->fd, copy->cmd,
 | ||
|                 SET_WINDOW_TOTAL_LEN, NULL, NULL);
 | ||
| }
 | ||
| 
 | ||
| #define SET_FRAME_LEN	10
 | ||
| 
 | ||
| static SANE_Status set_frame (SnapScan_Scanner *pss, SANE_Byte frame_no)
 | ||
| {
 | ||
|     static const char *me = "set_frame";
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     DBG (DL_VERBOSE, "%s setting frame to %d\n", me, frame_no);
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|     pss->cmd[0] = OBJECT_POSITION;
 | ||
|     pss->cmd[1] = 2;          /* Absolute position used here to select the frame */
 | ||
|     pss->cmd[4] = frame_no;
 | ||
| 
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
| 
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status set_focus (SnapScan_Scanner *pss, SANE_Int focus)
 | ||
| {
 | ||
|     static const char *me = "set_focus";
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s(%d)\n", me, focus);
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|     pss->cmd[0] = OBJECT_POSITION;
 | ||
|     pss->cmd[1] = 4;          /* Rotate object but here it sets the focus point */
 | ||
|     pss->cmd[3] = (focus >> 8) & 0xFF;
 | ||
|     pss->cmd[4] = focus & 0xFF;
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Int get_8 (u_char *p)
 | ||
| {
 | ||
|     SANE_Int b;
 | ||
|     b = p[0] | (p[1] << 8);
 | ||
|     return b;
 | ||
| }
 | ||
| 
 | ||
| static double get_val (u_char *p, SANE_Int len, SANE_Int x)
 | ||
| {
 | ||
| 	return get_8 (p + ((x + len) << 1)) / 255.0;
 | ||
| }
 | ||
| 
 | ||
| static double sum_pixel_differences (u_char *p, int len)
 | ||
| {
 | ||
| 	double v, m, s;
 | ||
| 	SANE_Int i;
 | ||
| 
 | ||
| 	s = 0;
 | ||
| 	for (i = 0; i < len-1; i++) {
 | ||
| 		v = get_val (p, len, i);
 | ||
| 		m = get_val (p, len, i+1);
 | ||
| 		s += fabs (v - m);
 | ||
| 	}
 | ||
| 	return s;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status scan (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "scan";
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|     pss->cmd[0] = SCAN;
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SCAN_LEN, NULL, NULL);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| /* supported read operations */
 | ||
| 
 | ||
| #define READ_IMAGE     0x00
 | ||
| #define READ_TRANSTIME 0x80
 | ||
| 
 | ||
| /* number of bytes expected must be in pss->expected_read_bytes */
 | ||
| static SANE_Status scsi_read (SnapScan_Scanner *pss, u_char read_type)
 | ||
| {
 | ||
|     static const char *me = "scsi_read";
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|     pss->cmd[0] = READ;
 | ||
|     pss->cmd[2] = read_type;
 | ||
|     if (read_type == READ_TRANSTIME && pss->pdev->model == PERFECTION2480)
 | ||
|         pss->cmd[5] = 1;
 | ||
| 
 | ||
|     u_int_to_u_char3p (pss->expected_read_bytes, pss->cmd + 6);
 | ||
| 
 | ||
|     pss->read_bytes = pss->expected_read_bytes;
 | ||
| 
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
 | ||
|                  READ_LEN, pss->buf, &pss->read_bytes);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status get_focus (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "get_focus";
 | ||
|     SANE_Int focus_point, max_focus_point;
 | ||
|     double sum, max;
 | ||
|     SANE_Status status;
 | ||
|     SnapScan_Scanner copy;
 | ||
| 
 | ||
|     copy = *pss;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     reserve_unit(©);
 | ||
| 
 | ||
|     status = set_window_autofocus (©);
 | ||
|     CHECK_STATUS (status, me, "set_window_autofocus");
 | ||
| 
 | ||
|     status = inquiry (©);
 | ||
|     CHECK_STATUS (status, me, "inquiry");
 | ||
| 
 | ||
|     status = scan (©);
 | ||
|     CHECK_STATUS (status, me, "scan");
 | ||
| 
 | ||
|     status = set_frame (©, copy.frame_no);
 | ||
|     CHECK_STATUS (status, me, "set_frame");
 | ||
| 
 | ||
|     DBG (DL_VERBOSE, "%s: Expected number of bytes for each read %d\n", me, (int)copy.bytes_per_line);
 | ||
|     DBG (DL_VERBOSE, "%s: Expected number of pixels per line %d\n", me, (int)copy.pixels_per_line);
 | ||
|     max_focus_point = -1;
 | ||
|     max = -1;
 | ||
|     for (focus_point = 0; focus_point <= 0x300; focus_point += 6) {
 | ||
|         status = set_focus (©, focus_point);
 | ||
|         CHECK_STATUS (status, me, "set_focus");
 | ||
| 
 | ||
|         copy.expected_read_bytes = copy.bytes_per_line;
 | ||
|         status = scsi_read (©, READ_IMAGE);
 | ||
|         CHECK_STATUS (status, me, "scsi_read");
 | ||
| 
 | ||
|         sum = sum_pixel_differences (copy.buf, copy.pixels_per_line);
 | ||
| 
 | ||
|         if (sum > max) {
 | ||
|             max = sum;
 | ||
|             max_focus_point = focus_point;
 | ||
|         }
 | ||
|     }
 | ||
|     pss->focus = max_focus_point;
 | ||
|     DBG (DL_VERBOSE, "%s: Focus point found to be at 0x%x\n", me, max_focus_point);
 | ||
|     release_unit (©);
 | ||
| 
 | ||
|     wait_scanner_ready (©);
 | ||
|     CHECK_STATUS (status, me, "wait_scanner_ready");
 | ||
| 
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
| static SANE_Status request_sense (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "request_sense";
 | ||
|     size_t read_bytes = 0;
 | ||
|     u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, 20, 0};
 | ||
|     u_char data[20];
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     read_bytes = 20;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd),
 | ||
|                data, &read_bytes);
 | ||
|     if (status != SANE_STATUS_GOOD)
 | ||
|     {
 | ||
|         DBG (DL_MAJOR_ERROR, "%s: scsi command error: %s\n",
 | ||
|              me, sane_strstatus (status));
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|         status = sense_handler (pss->fd, data, (void *) pss);
 | ||
|     }
 | ||
|     return status;
 | ||
| }
 | ||
| */
 | ||
| 
 | ||
| static SANE_Status send_diagnostic (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static const char *me = "send_diagnostic";
 | ||
|     u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0};    /* self-test */
 | ||
|     SANE_Status status;
 | ||
| 
 | ||
|     if (pss->pdev->model == PRISA620
 | ||
|         ||
 | ||
|      pss->pdev->model == PRISA610
 | ||
|         ||
 | ||
|      pss->pdev->model == SNAPSCAN1236
 | ||
|         ||
 | ||
|      pss->pdev->model == SCANWIT2720S
 | ||
|         ||
 | ||
|         pss->pdev->model == ARCUS1200)
 | ||
|     {
 | ||
|         return SANE_STATUS_GOOD;
 | ||
|     }
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
| 
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status wait_scanner_ready (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     static char me[] = "wait_scanner_ready";
 | ||
|     SANE_Status status;
 | ||
|     int retries;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
| 
 | ||
|     /* if the tray is returning to the start position
 | ||
|        no time to wait is returned by the scanner. We'll
 | ||
|        try several times and sleep 1 second between each try. */
 | ||
|     for (retries = 20; retries; retries--)
 | ||
|     {
 | ||
|         status = test_unit_ready (pss);
 | ||
|         switch (status)
 | ||
|         {
 | ||
|         case SANE_STATUS_GOOD:
 | ||
|             return status;
 | ||
|         case SANE_STATUS_DEVICE_BUSY:
 | ||
|             /* first additional sense byte contains time to wait */
 | ||
|             {
 | ||
|                 int delay = pss->asi1;
 | ||
|                 if (delay > 0)
 | ||
|                 {
 | ||
|                     DBG (0,
 | ||
|                         "Scanner warming up - waiting %d seconds.\n",
 | ||
|                         delay);
 | ||
|                     sleep (delay);
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     /* This seems to happen for Epson scanners. Return
 | ||
|                        SANE_STATUS_GOOD and hope the scanner accepts the
 | ||
|                        next command... */
 | ||
|                     DBG (DL_CALL_TRACE, "%s: No timeout specified, returning immediately\n", me);
 | ||
|                     return SANE_STATUS_GOOD;
 | ||
|                 }
 | ||
|             }
 | ||
|             break;
 | ||
|         case SANE_STATUS_IO_ERROR:
 | ||
|             /* hardware error; bail */
 | ||
|             DBG (DL_MAJOR_ERROR, "%s: hardware error detected.\n", me);
 | ||
|             return status;
 | ||
|         case SANE_STATUS_JAMMED:
 | ||
|         case SANE_STATUS_NO_DOCS:
 | ||
|             return status;
 | ||
|         default:
 | ||
|             DBG (DL_MAJOR_ERROR,
 | ||
|                 "%s: unhandled request_sense result; trying again.\n",
 | ||
|                 me);
 | ||
|             break;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| #define READ_CALIBRATION 0x82
 | ||
| #define READ_CALIBRATION_BLACK 0x89
 | ||
| #define NUM_CALIBRATION_LINES 16
 | ||
| #define NUM_CALIBRATION_LINES_EPSON 48
 | ||
| #define NUM_CALIBRATION_LINES_EPSON_BLACK 128
 | ||
| 
 | ||
| static SANE_Status calibrate_epson (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     u_char *buf, *pbuf;
 | ||
|     int *bins;
 | ||
|     static const char *me = "calibrate_epson";
 | ||
|     /* pixels_per_line = 8.5 inch * resolution of one sensor */
 | ||
|     int pixels_per_line = pss->actual_res * 17/4;
 | ||
|     int num_bins = pixels_per_line;
 | ||
|     int i, j, k, loop_inc, tl;
 | ||
|     int r, g, b;
 | ||
|     size_t expected_read_bytes;
 | ||
|     size_t read_bytes;
 | ||
|     SANE_Status status;
 | ||
|     int pass;
 | ||
|     int cal_lines = NUM_CALIBRATION_LINES_EPSON;
 | ||
|     int dtc = READ_CALIBRATION;
 | ||
|     int bytes_per_bin = 1;
 | ||
| 
 | ||
|     /* in 16 bit mode we get two bytes of cal data per bin */
 | ||
|     if (pss->bpp_scan == 16)
 | ||
|         bytes_per_bin = 2;
 | ||
| 
 | ||
|     /* calculate number of bins depending on mode and resolution
 | ||
|      * colour mode requires bins for each of rgb
 | ||
|      * full resolution doubles it because of second sensor line */
 | ||
|     if (is_colour_mode(actual_mode(pss))) {
 | ||
|         num_bins *= 3;
 | ||
|     }
 | ||
|     if (pss->res >= (SANE_Int)pss->actual_res) {
 | ||
|         num_bins *= 2;
 | ||
|     }
 | ||
| 
 | ||
|     /* allocate memory for bins, all the red, then green, then blue */
 | ||
|     bins = (int *) malloc (num_bins * sizeof (int));
 | ||
|     if (!bins) {
 | ||
|         DBG (DL_MAJOR_ERROR, "%s: out of memory allocating bins, %ld bytes.", me, (u_long)num_bins * sizeof (int));
 | ||
|         return SANE_STATUS_NO_MEM;
 | ||
|     }
 | ||
| 
 | ||
|     /* allocate buffer for receive data */
 | ||
|     expected_read_bytes = pixels_per_line * 3 * 4;
 | ||
|     buf = (u_char *) malloc (expected_read_bytes);
 | ||
|     if (!buf) {
 | ||
|         DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %ld bytes.", me, (u_long)expected_read_bytes);
 | ||
|         free (bins);
 | ||
|         return SANE_STATUS_NO_MEM;
 | ||
|     }
 | ||
| 
 | ||
|     loop_inc = expected_read_bytes / (num_bins * bytes_per_bin);
 | ||
| 
 | ||
|     /* do two passes, first pass does basic calibration, second does transparency adaptor if in use */
 | ||
|     for (pass = 0; pass < 2; pass++) {
 | ||
|         if (pass == 1) {
 | ||
|             if (pss->source == SRC_TPO) {
 | ||
|                 /* pass 1 is for black level calibration of transparency adaptor */
 | ||
|                 cal_lines = NUM_CALIBRATION_LINES_EPSON_BLACK;
 | ||
|                 dtc = READ_CALIBRATION_BLACK;
 | ||
|             } else
 | ||
|                 continue;
 | ||
|         }
 | ||
| 
 | ||
|         /* empty the bins */
 | ||
|         for (i = 0; i < num_bins; i++)
 | ||
|             bins[i] = 0;
 | ||
| 
 | ||
|         for (i = 0; i < cal_lines; i += loop_inc) {
 | ||
|             /* get the calibration data */
 | ||
|             zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|             pss->cmd[0] = READ;
 | ||
|             pss->cmd[2] = dtc;
 | ||
|             pss->cmd[5] = cal_lines;
 | ||
|             if (cal_lines - i > loop_inc)
 | ||
|                 expected_read_bytes = loop_inc * (num_bins * bytes_per_bin);
 | ||
|             else
 | ||
|                 expected_read_bytes = (cal_lines - i) * (num_bins * bytes_per_bin);
 | ||
| 
 | ||
|             u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
 | ||
|             read_bytes = expected_read_bytes;
 | ||
| 
 | ||
|             status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
 | ||
|                          READ_LEN, buf, &read_bytes);
 | ||
| 
 | ||
|             if (status != SANE_STATUS_GOOD) {
 | ||
|                 DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "read_cal_2480", sane_strstatus(status));
 | ||
|                 free (bins);
 | ||
|                 free (buf);
 | ||
|                 return status;
 | ||
|             }
 | ||
| 
 | ||
|             if (read_bytes != expected_read_bytes) {
 | ||
|                 DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
 | ||
|                 free (bins);
 | ||
|                 free (buf);
 | ||
|                 return SANE_STATUS_IO_ERROR;
 | ||
|             }
 | ||
| 
 | ||
|             /* add calibration results into bins */
 | ||
|             pbuf = buf;
 | ||
|             for (j = 0; j < (int)expected_read_bytes / (num_bins * bytes_per_bin); j++) {
 | ||
|               for (k = 0; k < num_bins; k++) {
 | ||
|                 bins[k] += *pbuf++;
 | ||
|                 /* add in second byte for 16bit mode */
 | ||
|                 if (bytes_per_bin == 2) {
 | ||
|                   bins[k] += *pbuf++ * 256;
 | ||
| 		}
 | ||
|               }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /* now make averages */
 | ||
|         for (k = 0; k < num_bins; k++) {
 | ||
|             bins[k] /= cal_lines;
 | ||
|             /* also divide by 64 for 16bit mode */
 | ||
|             if (bytes_per_bin == 2)
 | ||
|               bins[k] /= 64;
 | ||
|         }
 | ||
|         /* now fill up result buffer */
 | ||
|         r = g = b = 0;
 | ||
| 
 | ||
|         /* create data to send back: start with r g b values, and follow with differences
 | ||
|          * to previous value */
 | ||
|         pbuf = pss->buf + SEND_LENGTH;
 | ||
|         if (is_colour_mode(actual_mode(pss))) {
 | ||
|             for (k = 0; k < num_bins / 3; k++) {
 | ||
|                 *pbuf++ = bins[k] - r;
 | ||
|                 r = bins[k];
 | ||
|                 *pbuf++ = bins[k + num_bins/3] - g;
 | ||
|                 g = bins[k + num_bins/3];
 | ||
|                 *pbuf++ = bins[k + 2*num_bins/3] - b;
 | ||
|                 b = bins[k + 2*num_bins/3];
 | ||
|             }
 | ||
|         } else {
 | ||
|             for (k = 0; k < num_bins; k++) {
 | ||
|                 *pbuf++ = bins[k] - g;
 | ||
|                 g = bins[k];
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /* send back the calibration data; round up transfer length (to match windows driver) */
 | ||
|         zero_buf (pss->buf, SEND_LENGTH);
 | ||
|         pss->buf[0] = SEND;
 | ||
|         pss->buf[2] = dtc;
 | ||
|         tl = (num_bins + 0xff) & ~0xff;
 | ||
|         u_int_to_u_char3p (tl, pss->buf + 6);
 | ||
|         status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
 | ||
|                                  NULL, NULL);
 | ||
|         if (status != SANE_STATUS_GOOD) {
 | ||
|             DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "send_cal_2480", sane_strstatus(status));
 | ||
|             free (bins);
 | ||
|             free (buf);
 | ||
|             return status;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     free (bins);
 | ||
|     free (buf);
 | ||
|     return SANE_STATUS_GOOD;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status read_calibration_data (SnapScan_Scanner *pss, void *buf, u_char num_lines)
 | ||
| {
 | ||
|     static const char *me = "read_calibration_data";
 | ||
|     SANE_Status status;
 | ||
|     size_t expected_read_bytes = num_lines * calibration_line_length(pss);
 | ||
|     size_t read_bytes;
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
|     zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
 | ||
|     pss->cmd[0] = READ;
 | ||
|     pss->cmd[2] = READ_CALIBRATION;
 | ||
|     pss->cmd[5] = num_lines;
 | ||
|     u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
 | ||
|     read_bytes = expected_read_bytes;
 | ||
| 
 | ||
|     status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
 | ||
|                  READ_LEN, buf, &read_bytes);
 | ||
|     CHECK_STATUS (status, me, "snapscan_cmd");
 | ||
| 
 | ||
|     if(read_bytes != expected_read_bytes) {
 | ||
|     DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
 | ||
|     return SANE_STATUS_IO_ERROR;
 | ||
|     }
 | ||
| 
 | ||
|     return SANE_STATUS_GOOD;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status calibrate (SnapScan_Scanner *pss)
 | ||
| {
 | ||
|     int r;
 | ||
|     int c;
 | ||
|     u_char *buf;
 | ||
|     static const char *me = "calibrate";
 | ||
|     SANE_Status status;
 | ||
|     int line_length = calibration_line_length(pss);
 | ||
| 
 | ||
|     if ((pss->pdev->model == PERFECTION2480) ||
 | ||
|         (pss->pdev->model == PERFECTION3490)) {
 | ||
|         return calibrate_epson (pss);
 | ||
|     }
 | ||
| 
 | ||
|     if (pss->pdev->model == PRISA5150)
 | ||
|     {
 | ||
|         return send_calibration_5150(pss);
 | ||
|     }
 | ||
| 
 | ||
|     DBG (DL_CALL_TRACE, "%s\n", me);
 | ||
| 
 | ||
|     if (line_length) {
 | ||
|         int num_lines = pss->phys_buf_sz / line_length;
 | ||
|         if (num_lines > NUM_CALIBRATION_LINES)
 | ||
|         {
 | ||
|             num_lines = NUM_CALIBRATION_LINES;
 | ||
|         } else if (num_lines == 0)
 | ||
|         {
 | ||
|             DBG(DL_MAJOR_ERROR, "%s: scsi request size underflow (< %d bytes)", me, line_length);
 | ||
|             return SANE_STATUS_IO_ERROR;
 | ||
|         }
 | ||
|         buf = (u_char *) malloc(num_lines * line_length);
 | ||
|         if (!buf)
 | ||
|         {
 | ||
|             DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %d bytes.", me, num_lines * line_length);
 | ||
|             return SANE_STATUS_NO_MEM;
 | ||
|         }
 | ||
| 
 | ||
|         DBG (DL_MAJOR_ERROR, "%s: reading calibration data (%d lines)\n", me, num_lines);
 | ||
|         status = read_calibration_data(pss, buf, num_lines);
 | ||
|         CHECK_STATUS(status, me, "read_calibration_data");
 | ||
| 
 | ||
|         for(c=0; c < line_length; c++) {
 | ||
|             u_int sum = 0;
 | ||
|             for(r=0; r < num_lines; r++) {
 | ||
|                 sum += buf[c + r * line_length];
 | ||
|             }
 | ||
|             pss->buf[c + SEND_LENGTH] = sum / num_lines;
 | ||
|         }
 | ||
| 
 | ||
|         status = send (pss, DTC_CALIBRATION, 1);
 | ||
|         CHECK_STATUS(status, me, "send calibration");
 | ||
|         free(buf);
 | ||
|     }
 | ||
|     return SANE_STATUS_GOOD;
 | ||
| }
 | ||
| 
 | ||
| static SANE_Status download_firmware(SnapScan_Scanner * pss)
 | ||
| {
 | ||
|     static const char *me = "download_firmware";
 | ||
|     unsigned char *pFwBuf, *pCDB;
 | ||
|     char* firmware = NULL;
 | ||
|     FILE *fd;
 | ||
|     size_t bufLength,cdbLength;
 | ||
|     SANE_Status status = SANE_STATUS_GOOD;
 | ||
|     char cModelName[8], cModel[255];
 | ||
|     unsigned char bModelNo;
 | ||
| 
 | ||
|     bModelNo =*(pss->buf + INQUIRY_HWMI);
 | ||
|     zero_buf((unsigned char *)cModel, 255);
 | ||
|     sprintf(cModelName, "%d", bModelNo);
 | ||
|     DBG(DL_INFO, "Looking up %s\n", cModelName);
 | ||
|     if (pss->pdev->firmware_filename) {
 | ||
|         firmware = pss->pdev->firmware_filename;
 | ||
|     } else if (default_firmware_filename) {
 | ||
|         firmware = default_firmware_filename;
 | ||
|     } else {
 | ||
|         DBG (0,
 | ||
|             "%s: No firmware entry found in config file %s.\n",
 | ||
|             me,
 | ||
|             SNAPSCAN_CONFIG_FILE
 | ||
|         );
 | ||
|         status = SANE_STATUS_INVAL;
 | ||
|     }
 | ||
|     if (status == SANE_STATUS_GOOD)
 | ||
|     {
 | ||
|         cdbLength = 10;
 | ||
|         DBG(DL_INFO, "Downloading %s\n", firmware);
 | ||
|         fd = fopen(firmware,"rb");
 | ||
|         if(fd == NULL)
 | ||
|         {
 | ||
|             DBG (0, "Cannot open firmware file %s.\n", firmware);
 | ||
|             DBG (0, "Edit the firmware file entry in %s.\n", SNAPSCAN_CONFIG_FILE);
 | ||
|             status = SANE_STATUS_INVAL;
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             switch (pss->pdev->model)
 | ||
|             {
 | ||
|             case PRISA610:
 | ||
|             case PRISA310:
 | ||
|             case PRISA620:
 | ||
|             case PRISA1240:
 | ||
|             case PRISA640:
 | ||
|             case PRISA4300:
 | ||
|             case PRISA4300_2:
 | ||
|             case PRISA5000:
 | ||
|             case PRISA5000E:
 | ||
|             case PRISA5150:
 | ||
|             case PRISA5300:
 | ||
|             case STYLUS_CX1500:
 | ||
|                 /* ACER firmware files do not contain an info block */
 | ||
|                 fseek(fd, 0, SEEK_END);
 | ||
|                 bufLength = ftell(fd);
 | ||
|                 fseek(fd, 0, SEEK_SET);
 | ||
|                 break;
 | ||
|             case PERFECTION1270:
 | ||
|             case PERFECTION1670:
 | ||
|             case PERFECTION2480:
 | ||
|             case PERFECTION3490:
 | ||
|                 /* Epson firmware files contain an info block which
 | ||
|                    specifies the length of the firmware data. The
 | ||
|                    length information is stored at offset 0x64 from
 | ||
|                    end of file */
 | ||
|                 {
 | ||
|                     unsigned char size_l, size_h;
 | ||
|                     fseek(fd, -0x64, SEEK_END);
 | ||
|                     fread(&size_l, 1, 1, fd);
 | ||
|                     fread(&size_h, 1, 1, fd);
 | ||
|                     fseek(fd, 0, SEEK_SET);
 | ||
|                     bufLength = (size_h << 8) + size_l;
 | ||
|                 }
 | ||
|                 break;
 | ||
|             default:
 | ||
|                 /* AGFA firmware files contain an info block which
 | ||
|                    specifies the length of the firmware data. The
 | ||
|                    length information is stored at offset 0x5e from
 | ||
|                    end of file */
 | ||
|                 {
 | ||
|                     unsigned char size_l, size_h;
 | ||
|                     fseek(fd, -0x5e, SEEK_END);
 | ||
|                     fread(&size_l, 1, 1, fd);
 | ||
|                     fread(&size_h, 1, 1, fd);
 | ||
|                     fseek(fd, 0, SEEK_SET);
 | ||
|                     bufLength = (size_h << 8) + size_l;
 | ||
|                 }
 | ||
|                 break;
 | ||
|             }
 | ||
| 
 | ||
|             DBG(DL_INFO, "Size of firmware: %lu\n", (u_long) bufLength);
 | ||
|             pCDB = (unsigned char *)malloc(bufLength + cdbLength);
 | ||
|             pFwBuf = pCDB + cdbLength;
 | ||
|             zero_buf (pCDB, cdbLength);
 | ||
|             (void)fread(pFwBuf,1,bufLength,fd);
 | ||
| 
 | ||
|             *pCDB = SEND;
 | ||
|             *(pCDB + 2) = 0x87;
 | ||
|             *(pCDB + 6) = (bufLength >> 16) & 0xff;
 | ||
|             *(pCDB + 7) = (bufLength >> 8) & 0xff;
 | ||
|             *(pCDB + 8) = bufLength  & 0xff;
 | ||
| 
 | ||
|             status = snapscan_cmd (
 | ||
|                 pss->pdev->bus, pss->fd, pCDB, bufLength+cdbLength, NULL, NULL);
 | ||
|             pss->firmware_loaded = SANE_TRUE;
 | ||
| 
 | ||
|             free(pCDB);
 | ||
|             fclose(fd);
 | ||
|         }
 | ||
|     }
 | ||
|     return status;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  * $Log$
 | ||
|  * Revision 1.58  2006/09/03 10:00:11  oliver-guest
 | ||
|  * Bugfix for firmware download by Paul Smedley
 | ||
|  *
 | ||
|  * Revision 1.57  2006/03/20 18:20:10  oliver-guest
 | ||
|  * Limit inquiry length to 120 bytes if firmware is not yet loaded
 | ||
|  *
 | ||
|  * Revision 1.56  2006/01/10 19:32:16  oliver-guest
 | ||
|  * Added 12 bit gamma tables for Epson Stylus CX-1500
 | ||
|  *
 | ||
|  * Revision 1.55  2006/01/06 20:59:17  oliver-guest
 | ||
|  * Some fixes for the Epson Stylus CX 1500
 | ||
|  *
 | ||
|  * Revision 1.54  2006/01/01 22:57:01  oliver-guest
 | ||
|  * Added calibration data for Benq 5150 / 5250, preliminary support for Epson Stylus CX 1500
 | ||
|  *
 | ||
|  * Revision 1.53  2005/12/05 20:38:22  oliver-guest
 | ||
|  * Small bugfix for Benq 5150
 | ||
|  *
 | ||
|  * Revision 1.52  2005/12/04 15:03:00  oliver-guest
 | ||
|  * Some fixes for Benq 5150
 | ||
|  *
 | ||
|  * Revision 1.51  2005/11/26 18:53:03  oliver-guest
 | ||
|  * Fix inquiry bug for Benq 5000
 | ||
|  *
 | ||
|  * Revision 1.50  2005/11/17 23:47:10  oliver-guest
 | ||
|  * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
 | ||
|  *
 | ||
|  * Revision 1.49  2005/11/17 23:32:22  oliver-guest
 | ||
|  * Fixes for Epson 3490 @ 2400 DPI
 | ||
|  *
 | ||
|  * Revision 1.48  2005/11/15 20:11:19  oliver-guest
 | ||
|  * Enabled quality calibration for the Epson 3490
 | ||
|  *
 | ||
|  * Revision 1.47  2005/11/02 19:22:06  oliver-guest
 | ||
|  * Fixes for Benq 5000
 | ||
|  *
 | ||
|  * Revision 1.46  2005/10/31 21:08:47  oliver-guest
 | ||
|  * Distinguish between Benq 5000/5000E/5000U
 | ||
|  *
 | ||
|  * Revision 1.45  2005/10/24 19:46:40  oliver-guest
 | ||
|  * Preview and range fix for Epson 2480/2580
 | ||
|  *
 | ||
|  * Revision 1.44  2005/10/23 21:28:58  oliver-guest
 | ||
|  * Fix for buffer size in high res modes, fixes for delay code
 | ||
|  *
 | ||
|  * Revision 1.43  2005/10/20 21:23:53  oliver-guest
 | ||
|  * Fixes for 16 bit quality calibration by Simon Munton
 | ||
|  *
 | ||
|  * Revision 1.42  2005/10/13 22:43:30  oliver-guest
 | ||
|  * Fixes for 16 bit scan mode from Simon Munton
 | ||
|  *
 | ||
|  * Revision 1.41  2005/10/11 18:47:07  oliver-guest
 | ||
|  * Fixes for Epson 3490 and 16 bit scan mode
 | ||
|  *
 | ||
|  * Revision 1.40  2005/09/28 22:09:26  oliver-guest
 | ||
|  * Reenabled enhanced inquiry command for Epson scanners (duh\!)
 | ||
|  *
 | ||
|  * Revision 1.39  2005/09/28 21:33:10  oliver-guest
 | ||
|  * Added 16 bit option for Epson scanners (untested)
 | ||
|  *
 | ||
|  * Revision 1.38  2005/09/25 08:19:12  oliver-guest
 | ||
|  * Removed debugging code for Epson scanners
 | ||
|  *
 | ||
|  * Revision 1.37  2005/09/03 11:31:31  oliver-guest
 | ||
|  * Another small bugfix
 | ||
|  *
 | ||
|  * Revision 1.36  2005/09/03 10:52:11  oliver-guest
 | ||
|  * Fixed debugging code for epson scanners
 | ||
|  *
 | ||
|  * Revision 1.35  2005/08/16 17:19:20  oliver-guest
 | ||
|  * Make compileable again
 | ||
|  *
 | ||
|  * Revision 1.34  2005/08/15 18:56:55  oliver-guest
 | ||
|  * Added temporary debug code for 2480/2580 distinction
 | ||
|  *
 | ||
|  * Revision 1.33  2005/08/15 18:06:37  oliver-guest
 | ||
|  * Added support for Epson 3490/3590 (thanks to Matt Judge)
 | ||
|  *
 | ||
|  * Revision 1.32  2005/07/18 17:37:37  oliver-guest
 | ||
|  * ZETA changes for SnapScan backend
 | ||
|  *
 | ||
|  * Revision 1.31  2004/12/09 23:21:48  oliver-guest
 | ||
|  * Added quality calibration for Epson 2480 (by Simon Munton)
 | ||
|  *
 | ||
|  * Revision 1.30  2004/12/01 22:12:03  oliver-guest
 | ||
|  * Added support for Epson 1270
 | ||
|  *
 | ||
|  * Revision 1.29  2004/10/03 17:34:36  hmg-guest
 | ||
|  * 64 bit platform fixes (bug #300799).
 | ||
|  *
 | ||
|  * Revision 1.28  2004/09/02 20:59:11  oliver-guest
 | ||
|  * Added support for Epson 2480
 | ||
|  *
 | ||
|  * Revision 1.27  2004/04/02 20:19:24  oliver-guest
 | ||
|  * Various bugfixes for gamma corretion (thanks to Robert Tsien)
 | ||
|  *
 | ||
|  * Revision 1.26  2003/11/07 23:26:49  oliver-guest
 | ||
|  * Final bugfixes for bascic support of Epson 1670
 | ||
|  *
 | ||
|  * Revision 1.25  2003/10/21 20:43:25  oliver-guest
 | ||
|  * Bugfixes for SnapScan backend
 | ||
|  *
 | ||
|  * Revision 1.24  2003/10/07 19:41:34  oliver-guest
 | ||
|  * Updates for Epson Perfection 1670
 | ||
|  *
 | ||
|  * Revision 1.23  2003/08/19 21:05:08  oliverschwartz
 | ||
|  * Scanner ID cleanup
 | ||
|  *
 | ||
|  * Revision 1.22  2003/04/30 20:49:39  oliverschwartz
 | ||
|  * SnapScan backend 1.4.26
 | ||
|  *
 | ||
|  * Revision 1.37  2003/04/30 20:42:19  oliverschwartz
 | ||
|  * Added support for Agfa Arcus 1200 (supplied by Valtteri Vuorikoski)
 | ||
|  *
 | ||
|  * Revision 1.36  2003/04/02 21:17:13  oliverschwartz
 | ||
|  * Fix for 1200 DPI with Acer 5000
 | ||
|  *
 | ||
|  * Revision 1.35  2003/02/08 10:45:09  oliverschwartz
 | ||
|  * Use 600 DPI as optical resolution for Benq 5000
 | ||
|  *
 | ||
|  * Revision 1.34  2002/12/10 20:14:12  oliverschwartz
 | ||
|  * Enable color offset correction for SnapScan300
 | ||
|  *
 | ||
|  * Revision 1.33  2002/09/24 16:07:48  oliverschwartz
 | ||
|  * Added support for Benq 5000
 | ||
|  *
 | ||
|  * Revision 1.32  2002/06/06 20:40:01  oliverschwartz
 | ||
|  * Changed default scan area for transparancy unit of SnapScan e50
 | ||
|  *
 | ||
|  * Revision 1.31  2002/05/02 18:28:44  oliverschwartz
 | ||
|  * Added ADF support
 | ||
|  *
 | ||
|  * Revision 1.30  2002/04/27 14:41:22  oliverschwartz
 | ||
|  * Print number of open handles in close_scanner()
 | ||
|  *
 | ||
|  * Revision 1.29  2002/04/10 21:46:48  oliverschwartz
 | ||
|  * Removed illegal character
 | ||
|  *
 | ||
|  * Revision 1.28  2002/04/10 21:01:02  oliverschwartz
 | ||
|  * Disable send_diagnostic() for 1236s
 | ||
|  *
 | ||
|  * Revision 1.27  2002/03/24 12:11:20  oliverschwartz
 | ||
|  * Get name of firmware file in sane_init
 | ||
|  *
 | ||
|  * Revision 1.26  2002/01/23 20:42:41  oliverschwartz
 | ||
|  * Improve recognition of Acer 320U
 | ||
|  * Add sense_handler code for sense code 0x0b
 | ||
|  * Fix for spaces in model strings
 | ||
|  *
 | ||
|  * Revision 1.25  2001/12/12 19:44:59  oliverschwartz
 | ||
|  * Clean up CVS log
 | ||
|  *
 | ||
|  * Revision 1.24  2001/12/09 23:01:00  oliverschwartz
 | ||
|  * - use sense handler for USB
 | ||
|  * - fix scan mode
 | ||
|  *
 | ||
|  * Revision 1.23  2001/12/08 11:53:31  oliverschwartz
 | ||
|  * - Additional logging in sense handler
 | ||
|  * - Fix wait_scanner_ready() if device reports busy
 | ||
|  * - Fix scanning mode (preview/normal)
 | ||
|  *
 | ||
|  * Revision 1.22  2001/11/27 23:16:17  oliverschwartz
 | ||
|  * - Fix color alignment for SnapScan 600
 | ||
|  * - Added documentation in snapscan-sources.c
 | ||
|  * - Guard against TL_X < BR_X and TL_Y < BR_Y
 | ||
|  *
 | ||
|  * Revision 1.21  2001/10/21 08:49:37  oliverschwartz
 | ||
|  * correct number of scan lines for calibration thanks to Mikko Ty<54><79><EFBFBD>vi
 | ||
|  *
 | ||
|  * Revision 1.20  2001/10/12 20:54:04  oliverschwartz
 | ||
|  * enable gamma correction for Snapscan 1236, e20 and e50 scanners
 | ||
|  *
 | ||
|  * Revision 1.19  2001/10/11 14:02:10  oliverschwartz
 | ||
|  * Distinguish between e20/e25 and e40/e50
 | ||
|  *
 | ||
|  * Revision 1.18  2001/10/09 22:34:23  oliverschwartz
 | ||
|  * fix compiler warnings
 | ||
|  *
 | ||
|  * Revision 1.17  2001/10/08 19:26:01  oliverschwartz
 | ||
|  * - Disable quality calibration for scanners that do not support it
 | ||
|  *
 | ||
|  * Revision 1.16  2001/10/08 18:22:01  oliverschwartz
 | ||
|  * - Disable quality calibration for Acer Vuego 310F
 | ||
|  * - Use sanei_scsi_max_request_size as scanner buffer size
 | ||
|  *   for SCSI devices
 | ||
|  * - Added new devices to snapscan.desc
 | ||
|  *
 | ||
|  * Revision 1.15  2001/09/18 15:01:07  oliverschwartz
 | ||
|  * - Read scanner id string again after firmware upload
 | ||
|  *   to indentify correct model
 | ||
|  * - Make firmware upload work for AGFA scanners
 | ||
|  * - Change copyright notice
 | ||
|  *
 | ||
|  * Revision 1.14  2001/09/17 10:01:08  sable
 | ||
|  * Added model AGFA 1236U
 | ||
|  *
 | ||
|  * Revision 1.13  2001/09/09 18:06:32  oliverschwartz
 | ||
|  * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
 | ||
|  *
 | ||
|  * Revision 1.12  2001/04/10 13:00:31  sable
 | ||
|  * Moving sanei_usb_* to snapscani_usb*
 | ||
|  *
 | ||
|  * Revision 1.11  2001/04/10 11:04:31  sable
 | ||
|  * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
 | ||
|  *
 | ||
|  * Revision 1.10  2001/03/17 22:53:21  sable
 | ||
|  * Applying Mikael Magnusson patch concerning Gamma correction
 | ||
|  * Support for 1212U_2
 | ||
|  *
 | ||
|  * Revision 1.9  2000/11/10 01:01:59  sable
 | ||
|  * USB (kind of) autodetection
 | ||
|  *
 | ||
|  * Revision 1.8  2000/11/01 01:26:43  sable
 | ||
|  * Support for 1212U
 | ||
|  *
 | ||
|  * Revision 1.7  2000/10/30 22:32:20  sable
 | ||
|  * Support for vuego310s vuego610s and 1236s
 | ||
|  *
 | ||
|  * Revision 1.6  2000/10/29 22:44:55  sable
 | ||
|  * Bug correction for 1236s
 | ||
|  *
 | ||
|  * Revision 1.5  2000/10/28 14:16:10  sable
 | ||
|  * Bug correction for SnapScan310
 | ||
|  *
 | ||
|  * Revision 1.4  2000/10/28 14:06:35  sable
 | ||
|  * Add support for Acer300f
 | ||
|  *
 | ||
|  * Revision 1.3  2000/10/15 19:52:06  cbagwell
 | ||
|  * Changed USB support to a 1 line modification instead of multi-file
 | ||
|  * changes.
 | ||
|  *
 | ||
|  * Revision 1.2  2000/10/13 03:50:27  cbagwell
 | ||
|  * Updating to source from SANE 1.0.3.  Calling this versin 1.1
 | ||
|  *
 | ||
|  * */
 |