1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   Copyright (C) 1997, 1998, 2001, 2002, 2013 Franck Schnefra, Michel Roelofs,
4141cc406Sopenharmony_ci   Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
5141cc406Sopenharmony_ci   Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Mikael Magnusson,
6141cc406Sopenharmony_ci   Max Ushakov, Andrew Goodbody, Oliver Schwartz and Kevin Charter
7141cc406Sopenharmony_ci
8141cc406Sopenharmony_ci   This file is part of the SANE package.
9141cc406Sopenharmony_ci
10141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
11141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
12141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
13141cc406Sopenharmony_ci   License, or (at your option) any later version.
14141cc406Sopenharmony_ci
15141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
16141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
17141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18141cc406Sopenharmony_ci   General Public License for more details.
19141cc406Sopenharmony_ci
20141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
21141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
22141cc406Sopenharmony_ci
23141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
24141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
25141cc406Sopenharmony_ci
26141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
27141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
28141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
29141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
30141cc406Sopenharmony_ci   account of linking the SANE library code into it.
31141cc406Sopenharmony_ci
32141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
33141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
34141cc406Sopenharmony_ci   License.
35141cc406Sopenharmony_ci
36141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
37141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
38141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
39141cc406Sopenharmony_ci
40141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
41141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
42141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.
43141cc406Sopenharmony_ci
44141cc406Sopenharmony_ci   This file is a component of the implementation of a backend for many
45141cc406Sopenharmony_ci   of the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners.
46141cc406Sopenharmony_ci*/
47141cc406Sopenharmony_ci
48141cc406Sopenharmony_ci/*
49141cc406Sopenharmony_ci   SnapScan backend scsi command functions
50141cc406Sopenharmony_ci*/
51141cc406Sopenharmony_ci
52141cc406Sopenharmony_ci/* scanner scsi commands */
53141cc406Sopenharmony_ci
54141cc406Sopenharmony_cistatic SANE_Status download_firmware(SnapScan_Scanner * pss);
55141cc406Sopenharmony_cistatic SANE_Status wait_scanner_ready (SnapScan_Scanner * pss);
56141cc406Sopenharmony_ci
57141cc406Sopenharmony_ci#include "snapscan-usb.h"
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci/* find model by SCSI ID string or USB vendor/product ID */
60141cc406Sopenharmony_cistatic SnapScan_Model snapscani_get_model_id(char* model_str, int fd, SnapScan_Bus bus_type)
61141cc406Sopenharmony_ci{
62141cc406Sopenharmony_ci    static char me[] = "snapscani_get_model_id";
63141cc406Sopenharmony_ci    SnapScan_Model model_id = UNKNOWN;
64141cc406Sopenharmony_ci    SANE_Word vendor_id, product_id;
65141cc406Sopenharmony_ci    int i;
66141cc406Sopenharmony_ci
67141cc406Sopenharmony_ci    DBG(DL_CALL_TRACE, "%s(%s, %d, %d)\n",me, model_str, fd, bus_type);
68141cc406Sopenharmony_ci    for (i = 0;  i < known_scanners;  i++)
69141cc406Sopenharmony_ci    {
70141cc406Sopenharmony_ci        if (0 == strcasecmp (model_str, scanners[i].scsi_name))
71141cc406Sopenharmony_ci        {
72141cc406Sopenharmony_ci            model_id = scanners[i].id;
73141cc406Sopenharmony_ci            break;
74141cc406Sopenharmony_ci        }
75141cc406Sopenharmony_ci    }
76141cc406Sopenharmony_ci    /* Also test USB vendor and product ID numbers, since some USB models use
77141cc406Sopenharmony_ci       identical model names.
78141cc406Sopenharmony_ci    */
79141cc406Sopenharmony_ci    if ((bus_type == USB) &&
80141cc406Sopenharmony_ci        (sanei_usb_get_vendor_product(fd, &vendor_id, &product_id) == SANE_STATUS_GOOD))
81141cc406Sopenharmony_ci    {
82141cc406Sopenharmony_ci    	DBG(DL_MINOR_INFO,
83141cc406Sopenharmony_ci            "%s: looking up scanner for ID 0x%04x,0x%04x.\n",
84141cc406Sopenharmony_ci            me, vendor_id, product_id);
85141cc406Sopenharmony_ci        for (i = 0; i < known_usb_scanners; i++)
86141cc406Sopenharmony_ci        {
87141cc406Sopenharmony_ci            if ((usb_scanners[i].vendor_id == vendor_id) &&
88141cc406Sopenharmony_ci                (usb_scanners[i].product_id == product_id))
89141cc406Sopenharmony_ci            {
90141cc406Sopenharmony_ci                model_id = usb_scanners[i].id;
91141cc406Sopenharmony_ci                DBG(DL_MINOR_INFO, "%s: scanner identified\n", me);
92141cc406Sopenharmony_ci                break;
93141cc406Sopenharmony_ci            }
94141cc406Sopenharmony_ci        }
95141cc406Sopenharmony_ci    }
96141cc406Sopenharmony_ci    return model_id;
97141cc406Sopenharmony_ci}
98141cc406Sopenharmony_ci
99141cc406Sopenharmony_ci/* a sensible sense handler, courtesy of Franck;
100141cc406Sopenharmony_ci   the last argument is expected to be a pointer to the associated
101141cc406Sopenharmony_ci   SnapScan_Scanner structure */
102141cc406Sopenharmony_cistatic SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg)
103141cc406Sopenharmony_ci{
104141cc406Sopenharmony_ci    static char me[] = "sense_handler";
105141cc406Sopenharmony_ci    SnapScan_Scanner *pss = (SnapScan_Scanner *) arg;
106141cc406Sopenharmony_ci    u_char sense, asc, ascq;
107141cc406Sopenharmony_ci    char *sense_str = NULL, *as_str = NULL;
108141cc406Sopenharmony_ci    SANE_Status status = SANE_STATUS_GOOD;
109141cc406Sopenharmony_ci
110141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s(%ld, %p, %p)\n", me, (long) scsi_fd,
111141cc406Sopenharmony_ci         (void *) result, (void *) arg);
112141cc406Sopenharmony_ci
113141cc406Sopenharmony_ci    sense = result[2] & 0x0f;
114141cc406Sopenharmony_ci    asc = result[12];
115141cc406Sopenharmony_ci    ascq = result[13];
116141cc406Sopenharmony_ci    if (pss)
117141cc406Sopenharmony_ci    {
118141cc406Sopenharmony_ci        pss->asi1 = result[18];
119141cc406Sopenharmony_ci        pss->asi2 = result[19];
120141cc406Sopenharmony_ci    }
121141cc406Sopenharmony_ci    if ((result[0] & 0x80) == 0)
122141cc406Sopenharmony_ci    {
123141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE, "%s: sense key is invalid.\n", me);
124141cc406Sopenharmony_ci        return SANE_STATUS_GOOD;    /* sense key invalid */
125141cc406Sopenharmony_ci    } else {
126141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE, "%s: sense key: 0x%02x, asc: 0x%02x, ascq: 0x%02x, i1: 0x%02x, i2: 0x%02x\n",
127141cc406Sopenharmony_ci        me, sense, asc, ascq, result[18], result[19]);
128141cc406Sopenharmony_ci    }
129141cc406Sopenharmony_ci
130141cc406Sopenharmony_ci    switch (sense)
131141cc406Sopenharmony_ci    {
132141cc406Sopenharmony_ci    case 0x00:
133141cc406Sopenharmony_ci        /* no sense */
134141cc406Sopenharmony_ci        sense_str = "No sense.";
135141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
136141cc406Sopenharmony_ci        break;
137141cc406Sopenharmony_ci    case 0x02:
138141cc406Sopenharmony_ci        /* not ready */
139141cc406Sopenharmony_ci        sense_str = "Not ready.";
140141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
141141cc406Sopenharmony_ci        if (asc == 0x04  &&  ascq == 0x01)
142141cc406Sopenharmony_ci        {
143141cc406Sopenharmony_ci            /* warming up; byte 18 contains remaining seconds */
144141cc406Sopenharmony_ci            as_str = "Logical unit is in process of becoming ready.";
145141cc406Sopenharmony_ci            DBG (DL_MINOR_INFO, "%s: %s (%d seconds)\n", me, as_str, result[18]);
146141cc406Sopenharmony_ci            status = SANE_STATUS_DEVICE_BUSY;
147141cc406Sopenharmony_ci        }
148141cc406Sopenharmony_ci        break;
149141cc406Sopenharmony_ci    case 0x04:
150141cc406Sopenharmony_ci        /* hardware error */
151141cc406Sopenharmony_ci        sense_str = "Hardware error.";
152141cc406Sopenharmony_ci        /* byte 18 and 19 detail the hardware problems */
153141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s: %s (0x%02x, 0x%02x)\n", me, sense_str, result[18],
154141cc406Sopenharmony_ci            result[19]);
155141cc406Sopenharmony_ci        status = SANE_STATUS_IO_ERROR;
156141cc406Sopenharmony_ci        break;
157141cc406Sopenharmony_ci    case 0x05:
158141cc406Sopenharmony_ci        /* illegal request */
159141cc406Sopenharmony_ci        sense_str = "Illegal request.";
160141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
161141cc406Sopenharmony_ci        if (asc == 0x25 && ascq == 0x00)
162141cc406Sopenharmony_ci        {
163141cc406Sopenharmony_ci            as_str = "Logical unit not supported.";
164141cc406Sopenharmony_ci            DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
165141cc406Sopenharmony_ci        }
166141cc406Sopenharmony_ci        status = SANE_STATUS_IO_ERROR;
167141cc406Sopenharmony_ci        break;
168141cc406Sopenharmony_ci    case 0x09:
169141cc406Sopenharmony_ci        /* process error */
170141cc406Sopenharmony_ci        sense_str = "Process error.";
171141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
172141cc406Sopenharmony_ci        if (asc == 0x00 && ascq == 0x05)
173141cc406Sopenharmony_ci        {
174141cc406Sopenharmony_ci            /* no documents in ADF */
175141cc406Sopenharmony_ci            as_str = "End of data detected.";
176141cc406Sopenharmony_ci            DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
177141cc406Sopenharmony_ci            status = SANE_STATUS_NO_DOCS;
178141cc406Sopenharmony_ci        }
179141cc406Sopenharmony_ci        else if (asc == 0x3b && ascq == 0x05)
180141cc406Sopenharmony_ci        {
181141cc406Sopenharmony_ci            /* paper jam in ADF */
182141cc406Sopenharmony_ci            as_str = "Paper jam.";
183141cc406Sopenharmony_ci            DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
184141cc406Sopenharmony_ci            status = SANE_STATUS_JAMMED;
185141cc406Sopenharmony_ci        }
186141cc406Sopenharmony_ci        else if (asc == 0x3b && ascq == 0x09)
187141cc406Sopenharmony_ci        {
188141cc406Sopenharmony_ci            /* scanning area exceeds end of paper in ADF */
189141cc406Sopenharmony_ci            as_str = "Read past end of medium.";
190141cc406Sopenharmony_ci            DBG (DL_MINOR_INFO, "%s: %s\n", me, as_str);
191141cc406Sopenharmony_ci            status = SANE_STATUS_EOF;
192141cc406Sopenharmony_ci        }
193141cc406Sopenharmony_ci        break;
194141cc406Sopenharmony_ci    case 0x0b:
195141cc406Sopenharmony_ci        /* Aborted command */
196141cc406Sopenharmony_ci        sense_str = "Aborted Command.";
197141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s: %s\n", me, sense_str);
198141cc406Sopenharmony_ci        status = SANE_STATUS_IO_ERROR;
199141cc406Sopenharmony_ci        break;
200141cc406Sopenharmony_ci    default:
201141cc406Sopenharmony_ci        DBG (DL_MINOR_ERROR, "%s: no handling for sense %x.\n", me, sense);
202141cc406Sopenharmony_ci        break;
203141cc406Sopenharmony_ci    }
204141cc406Sopenharmony_ci
205141cc406Sopenharmony_ci    if (pss)
206141cc406Sopenharmony_ci    {
207141cc406Sopenharmony_ci        pss->sense_str = sense_str;
208141cc406Sopenharmony_ci        pss->as_str = as_str;
209141cc406Sopenharmony_ci    }
210141cc406Sopenharmony_ci    return status;
211141cc406Sopenharmony_ci}
212141cc406Sopenharmony_ci
213141cc406Sopenharmony_ci
214141cc406Sopenharmony_cistatic SANE_Status open_scanner (SnapScan_Scanner *pss)
215141cc406Sopenharmony_ci{
216141cc406Sopenharmony_ci    SANE_Status status;
217141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "open_scanner\n");
218141cc406Sopenharmony_ci    if (!pss->opens)
219141cc406Sopenharmony_ci    {
220141cc406Sopenharmony_ci        if(pss->pdev->bus == SCSI)
221141cc406Sopenharmony_ci        {
222141cc406Sopenharmony_ci            status = sanei_scsi_open (pss->devname, &(pss->fd),
223141cc406Sopenharmony_ci                sense_handler, (void *) pss);
224141cc406Sopenharmony_ci        }
225141cc406Sopenharmony_ci        else
226141cc406Sopenharmony_ci        {
227141cc406Sopenharmony_ci            status = snapscani_usb_open (pss->devname, &(pss->fd),
228141cc406Sopenharmony_ci                sense_handler, (void *) pss);
229141cc406Sopenharmony_ci        }
230141cc406Sopenharmony_ci    }
231141cc406Sopenharmony_ci    else
232141cc406Sopenharmony_ci    {
233141cc406Sopenharmony_ci        /* already open */
234141cc406Sopenharmony_ci        status = SANE_STATUS_GOOD;
235141cc406Sopenharmony_ci    }
236141cc406Sopenharmony_ci    if (status == SANE_STATUS_GOOD)
237141cc406Sopenharmony_ci        pss->opens++;
238141cc406Sopenharmony_ci    return status;
239141cc406Sopenharmony_ci}
240141cc406Sopenharmony_ci
241141cc406Sopenharmony_cistatic void close_scanner (SnapScan_Scanner *pss)
242141cc406Sopenharmony_ci{
243141cc406Sopenharmony_ci    static char me[] = "close_scanner";
244141cc406Sopenharmony_ci
245141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
246141cc406Sopenharmony_ci    if (pss->opens)
247141cc406Sopenharmony_ci    {
248141cc406Sopenharmony_ci        pss->opens--;
249141cc406Sopenharmony_ci        if (!pss->opens)
250141cc406Sopenharmony_ci        {
251141cc406Sopenharmony_ci            if(pss->pdev->bus == SCSI)
252141cc406Sopenharmony_ci            {
253141cc406Sopenharmony_ci                sanei_scsi_close (pss->fd);
254141cc406Sopenharmony_ci            }
255141cc406Sopenharmony_ci            else if(pss->pdev->bus == USB)
256141cc406Sopenharmony_ci            {
257141cc406Sopenharmony_ci                snapscani_usb_close (pss->fd);
258141cc406Sopenharmony_ci            }
259141cc406Sopenharmony_ci        } else {
260141cc406Sopenharmony_ci            DBG(DL_INFO, "%s: handles left: %d\n,",me, pss->opens);
261141cc406Sopenharmony_ci        }
262141cc406Sopenharmony_ci    }
263141cc406Sopenharmony_ci}
264141cc406Sopenharmony_ci
265141cc406Sopenharmony_cistatic SANE_Status snapscan_cmd(SnapScan_Bus bus, int fd, const void *src,
266141cc406Sopenharmony_ci                size_t src_size, void *dst, size_t * dst_size)
267141cc406Sopenharmony_ci{
268141cc406Sopenharmony_ci    SANE_Status status;
269141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "snapscan_cmd\n");
270141cc406Sopenharmony_ci    if(bus == USB)
271141cc406Sopenharmony_ci    {
272141cc406Sopenharmony_ci        status = snapscani_usb_cmd(fd, src, src_size, dst, dst_size);
273141cc406Sopenharmony_ci    }
274141cc406Sopenharmony_ci    else
275141cc406Sopenharmony_ci    {
276141cc406Sopenharmony_ci        status = sanei_scsi_cmd(fd, src, src_size, dst, dst_size);
277141cc406Sopenharmony_ci    }
278141cc406Sopenharmony_ci    return status;
279141cc406Sopenharmony_ci}
280141cc406Sopenharmony_ci
281141cc406Sopenharmony_ci/* SCSI commands */
282141cc406Sopenharmony_ci#define TEST_UNIT_READY        0x00
283141cc406Sopenharmony_ci#define INQUIRY                0x12
284141cc406Sopenharmony_ci#define SEND                   0x2A
285141cc406Sopenharmony_ci#define SET_WINDOW             0x24
286141cc406Sopenharmony_ci#define SCAN                   0x1B
287141cc406Sopenharmony_ci#define READ                   0x28
288141cc406Sopenharmony_ci#define REQUEST_SENSE          0x03
289141cc406Sopenharmony_ci#define RESERVE_UNIT           0x16
290141cc406Sopenharmony_ci#define RELEASE_UNIT           0x17
291141cc406Sopenharmony_ci#define SEND_DIAGNOSTIC        0x1D
292141cc406Sopenharmony_ci#define OBJECT_POSITION        0x31
293141cc406Sopenharmony_ci#define GET_DATA_BUFFER_STATUS 0x34
294141cc406Sopenharmony_ci
295141cc406Sopenharmony_ci#define SCAN_LEN 6
296141cc406Sopenharmony_ci#define READ_LEN 10
297141cc406Sopenharmony_ci
298141cc406Sopenharmony_ci/*  buffer tools */
299141cc406Sopenharmony_ci
300141cc406Sopenharmony_cistatic void zero_buf (u_char * buf, size_t len)
301141cc406Sopenharmony_ci{
302141cc406Sopenharmony_ci    size_t i;
303141cc406Sopenharmony_ci
304141cc406Sopenharmony_ci    for (i = 0;  i < len;  i++)
305141cc406Sopenharmony_ci        buf[i] = 0x00;
306141cc406Sopenharmony_ci}
307141cc406Sopenharmony_ci
308141cc406Sopenharmony_ci
309141cc406Sopenharmony_ci#define BYTE_SIZE 8
310141cc406Sopenharmony_ci
311141cc406Sopenharmony_cistatic u_short u_char_to_u_short (u_char * pc)
312141cc406Sopenharmony_ci{
313141cc406Sopenharmony_ci    u_short r = 0;
314141cc406Sopenharmony_ci    r |= pc[0];
315141cc406Sopenharmony_ci    r = r << BYTE_SIZE;
316141cc406Sopenharmony_ci    r |= pc[1];
317141cc406Sopenharmony_ci    return r;
318141cc406Sopenharmony_ci}
319141cc406Sopenharmony_ci
320141cc406Sopenharmony_cistatic void u_short_to_u_charp (u_short x, u_char * pc)
321141cc406Sopenharmony_ci{
322141cc406Sopenharmony_ci    pc[0] = (0xFF00 & x) >> BYTE_SIZE;
323141cc406Sopenharmony_ci    pc[1] = (0x00FF & x);
324141cc406Sopenharmony_ci}
325141cc406Sopenharmony_ci
326141cc406Sopenharmony_cistatic void u_int_to_u_char3p (u_int x, u_char * pc)
327141cc406Sopenharmony_ci{
328141cc406Sopenharmony_ci    pc[0] = (0xFF0000 & x) >> 2 * BYTE_SIZE;
329141cc406Sopenharmony_ci    pc[1] = (0x00FF00 & x) >> BYTE_SIZE;
330141cc406Sopenharmony_ci    pc[2] = (0x0000FF & x);
331141cc406Sopenharmony_ci}
332141cc406Sopenharmony_ci
333141cc406Sopenharmony_cistatic void u_int_to_u_char4p (u_int x, u_char * pc)
334141cc406Sopenharmony_ci{
335141cc406Sopenharmony_ci    pc[0] = (0xFF000000 & x) >> 3 * BYTE_SIZE;
336141cc406Sopenharmony_ci    pc[1] = (0x00FF0000 & x) >> 2 * BYTE_SIZE;
337141cc406Sopenharmony_ci    pc[2] = (0x0000FF00 & x) >> BYTE_SIZE;
338141cc406Sopenharmony_ci    pc[3] = (0x000000FF & x);
339141cc406Sopenharmony_ci}
340141cc406Sopenharmony_ci
341141cc406Sopenharmony_ci/* Convert 'STRING   ' to 'STRING' by adding 0 after last non-space */
342141cc406Sopenharmony_cistatic void remove_trailing_space (char *s)
343141cc406Sopenharmony_ci{
344141cc406Sopenharmony_ci    int position;
345141cc406Sopenharmony_ci
346141cc406Sopenharmony_ci    if (NULL == s)
347141cc406Sopenharmony_ci        return;
348141cc406Sopenharmony_ci
349141cc406Sopenharmony_ci    for (position = strlen (s);
350141cc406Sopenharmony_ci         position > 0  &&  ' ' == s[position - 1];
351141cc406Sopenharmony_ci         position--)
352141cc406Sopenharmony_ci    {
353141cc406Sopenharmony_ci        /* dummy */;
354141cc406Sopenharmony_ci    }
355141cc406Sopenharmony_ci    s[position] = 0;
356141cc406Sopenharmony_ci}
357141cc406Sopenharmony_ci
358141cc406Sopenharmony_cistatic void check_range (int *v, SANE_Range r)
359141cc406Sopenharmony_ci{
360141cc406Sopenharmony_ci    if (*v < r.min)
361141cc406Sopenharmony_ci    *v = r.min;
362141cc406Sopenharmony_ci    if (*v > r.max)
363141cc406Sopenharmony_ci    *v = r.max;
364141cc406Sopenharmony_ci}
365141cc406Sopenharmony_ci
366141cc406Sopenharmony_ci#define INQUIRY_LEN 6
367141cc406Sopenharmony_ci#define INQUIRY_RET_LEN 120
368141cc406Sopenharmony_ci#define INQUIRY_RET_LEN_EPSON 139
369141cc406Sopenharmony_ci#define INQUIRY_RET_LEN_5000  138
370141cc406Sopenharmony_ci
371141cc406Sopenharmony_ci#define INQUIRY_VENDOR         8    /* Offset in reply data to vendor name */
372141cc406Sopenharmony_ci#define INQUIRY_PRODUCT       16    /* Offset in reply data to product id */
373141cc406Sopenharmony_ci#define INQUIRY_REV           32    /* Offset in reply data to revision level */
374141cc406Sopenharmony_ci#define INQUIRY_PRL2          36    /* Product Revision Level 2 (AGFA) */
375141cc406Sopenharmony_ci#define INQUIRY_HCFG          37    /* Hardware Configuration (AGFA) */
376141cc406Sopenharmony_ci#define INQUIRY_HWST          40    /* Hardware status */
377141cc406Sopenharmony_ci#define INQUIRY_HWMI          41    /* HARDWARE Model ID */
378141cc406Sopenharmony_ci#define INQUIRY_PIX_PER_LINE  42    /* Pixels per scan line (AGFA) */
379141cc406Sopenharmony_ci#define INQUIRY_BYTE_PER_LINE 44    /* Bytes per scan line (AGFA) */
380141cc406Sopenharmony_ci#define INQUIRY_NUM_LINES     46    /* number of scan lines (AGFA) */
381141cc406Sopenharmony_ci#define INQUIRY_OPT_RES       48    /* optical resolution (AGFA) */
382141cc406Sopenharmony_ci#define INQUIRY_SCAN_SPEED    51    /* scan speed (AGFA) */
383141cc406Sopenharmony_ci#define INQUIRY_EXPTIME1      52    /* exposure time, first digit (AGFA) */
384141cc406Sopenharmony_ci#define INQUIRY_EXPTIME2      53    /* exposure time, second digit (AGFA) */
385141cc406Sopenharmony_ci#define INQUIRY_G2R_DIFF      54    /* green to red difference (AGFA) */
386141cc406Sopenharmony_ci#define INQUIRY_B2R_DIFF      55    /* green to red difference (AGFA) */
387141cc406Sopenharmony_ci#define INQUIRY_FIRMWARE      96    /* firmware date and time (AGFA) */
388141cc406Sopenharmony_ci#define INQUIRY_BYTE_PER_LINE_MSB    132   /* ?? top byte of bytes per scan line - epson 2480 */
389141cc406Sopenharmony_ci#define INQUIRY_EPSON_HCFG   138    /* ?? Hardware configuration (Epson) */
390141cc406Sopenharmony_ci
391141cc406Sopenharmony_ci
392141cc406Sopenharmony_ci/* a mini-inquiry reads only the first 36 bytes of inquiry data, and
393141cc406Sopenharmony_ci   returns the vendor(7 chars) and model(16 chars); vendor and model
394141cc406Sopenharmony_ci   must point to character buffers of size at least 8 and 17
395141cc406Sopenharmony_ci   respectively */
396141cc406Sopenharmony_ci
397141cc406Sopenharmony_cistatic SANE_Status mini_inquiry (SnapScan_Bus bus, int fd, char *vendor, char *model)
398141cc406Sopenharmony_ci{
399141cc406Sopenharmony_ci    static const char *me = "mini_inquiry";
400141cc406Sopenharmony_ci    size_t read_bytes;
401141cc406Sopenharmony_ci    char cmd[] = {INQUIRY, 0, 0, 0, 36, 0};
402141cc406Sopenharmony_ci    char data[36];
403141cc406Sopenharmony_ci    SANE_Status status;
404141cc406Sopenharmony_ci
405141cc406Sopenharmony_ci    read_bytes = 36;
406141cc406Sopenharmony_ci
407141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
408141cc406Sopenharmony_ci    status = snapscan_cmd (bus, fd, cmd, sizeof (cmd), data, &read_bytes);
409141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
410141cc406Sopenharmony_ci
411141cc406Sopenharmony_ci    memcpy (vendor, data + 8, 7);
412141cc406Sopenharmony_ci    vendor[7] = 0;
413141cc406Sopenharmony_ci    memcpy (model, data + 16, 16);
414141cc406Sopenharmony_ci    model[16] = 0;
415141cc406Sopenharmony_ci
416141cc406Sopenharmony_ci    remove_trailing_space (vendor);
417141cc406Sopenharmony_ci    remove_trailing_space (model);
418141cc406Sopenharmony_ci
419141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
420141cc406Sopenharmony_ci}
421141cc406Sopenharmony_ci
422141cc406Sopenharmony_ci/* TODO: Remove */
423141cc406Sopenharmony_cistatic char *snapscani_debug_data(char *str,const SANE_Byte *data, int len) {
424141cc406Sopenharmony_ci    char tmpstr[10];
425141cc406Sopenharmony_ci    int i;
426141cc406Sopenharmony_ci
427141cc406Sopenharmony_ci    str[0]=0;
428141cc406Sopenharmony_ci    for(i=0; i < (len < 20 ? len : 20); i++) {
429141cc406Sopenharmony_ci        sprintf(tmpstr," 0x%02x",((int)data[i]) & 0xff);
430141cc406Sopenharmony_ci        if(i%16 == 0 && i != 0)
431141cc406Sopenharmony_ci            strcat(str,"\n");
432141cc406Sopenharmony_ci        strcat(str,tmpstr);
433141cc406Sopenharmony_ci    }
434141cc406Sopenharmony_ci    if(i < len)
435141cc406Sopenharmony_ci        strcat(str," ...");
436141cc406Sopenharmony_ci    return str;
437141cc406Sopenharmony_ci}
438141cc406Sopenharmony_ci
439141cc406Sopenharmony_cistatic SANE_Status inquiry (SnapScan_Scanner *pss)
440141cc406Sopenharmony_ci{
441141cc406Sopenharmony_ci    static const char *me = "inquiry";
442141cc406Sopenharmony_ci    char tmpstr[150]; /* TODO: Remove */
443141cc406Sopenharmony_ci    SANE_Status status;
444141cc406Sopenharmony_ci    switch (pss->pdev->model)
445141cc406Sopenharmony_ci    {
446141cc406Sopenharmony_ci    case PERFECTION2480:
447141cc406Sopenharmony_ci    case PERFECTION3490:
448141cc406Sopenharmony_ci        if (pss->firmware_loaded)
449141cc406Sopenharmony_ci          pss->read_bytes = INQUIRY_RET_LEN_EPSON;
450141cc406Sopenharmony_ci        else
451141cc406Sopenharmony_ci          pss->read_bytes = INQUIRY_RET_LEN;
452141cc406Sopenharmony_ci        break;
453141cc406Sopenharmony_ci    case PRISA5000:
454141cc406Sopenharmony_ci    case PRISA5150:
455141cc406Sopenharmony_ci        pss->read_bytes = INQUIRY_RET_LEN_5000;
456141cc406Sopenharmony_ci        break;
457141cc406Sopenharmony_ci    default:
458141cc406Sopenharmony_ci        pss->read_bytes = INQUIRY_RET_LEN;
459141cc406Sopenharmony_ci        break;
460141cc406Sopenharmony_ci    }
461141cc406Sopenharmony_ci
462141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
463141cc406Sopenharmony_ci    pss->cmd[0] = INQUIRY;
464141cc406Sopenharmony_ci    pss->cmd[4] = pss->read_bytes;
465141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
466141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus,
467141cc406Sopenharmony_ci               pss->fd,
468141cc406Sopenharmony_ci               pss->cmd,
469141cc406Sopenharmony_ci               INQUIRY_LEN,
470141cc406Sopenharmony_ci               pss->buf,
471141cc406Sopenharmony_ci               &pss->read_bytes);
472141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
473141cc406Sopenharmony_ci
474141cc406Sopenharmony_ci    /* record current parameters */
475141cc406Sopenharmony_ci    {
476141cc406Sopenharmony_ci        char exptime[4] = {' ', '.', ' ', 0};
477141cc406Sopenharmony_ci        exptime[0] = (char) (pss->buf[INQUIRY_EXPTIME1] + '0');
478141cc406Sopenharmony_ci        exptime[2] = (char) (pss->buf[INQUIRY_EXPTIME2] + '0');
479141cc406Sopenharmony_ci        pss->ms_per_line = atof (exptime)*(float) pss->buf[INQUIRY_SCAN_SPEED];
480141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE, "%s: exposure time: %s ms\n", me, exptime);
481141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE, "%s: ms per line: %f\n", me, pss->ms_per_line);
482141cc406Sopenharmony_ci    }
483141cc406Sopenharmony_ci
484141cc406Sopenharmony_ci    switch (pss->pdev->model)
485141cc406Sopenharmony_ci    {
486141cc406Sopenharmony_ci    case SNAPSCAN:
487141cc406Sopenharmony_ci    case ACER300F:
488141cc406Sopenharmony_ci        pss->chroma_offset[R_CHAN] =
489141cc406Sopenharmony_ci        pss->chroma_offset[G_CHAN] =
490141cc406Sopenharmony_ci        pss->chroma_offset[B_CHAN] = 0;
491141cc406Sopenharmony_ci        pss->chroma = 0;
492141cc406Sopenharmony_ci        break;
493141cc406Sopenharmony_ci    case PERFECTION2480:
494141cc406Sopenharmony_ci    case PERFECTION3490:
495141cc406Sopenharmony_ci        if (pss->firmware_loaded)
496141cc406Sopenharmony_ci        {
497141cc406Sopenharmony_ci            snapscani_debug_data(tmpstr, pss->buf+120, 19);
498141cc406Sopenharmony_ci            DBG (DL_DATA_TRACE, "%s: Epson additional inquiry data:\n%s\n", me, tmpstr);
499141cc406Sopenharmony_ci            pss->hconfig_epson = pss->buf[INQUIRY_EPSON_HCFG];
500141cc406Sopenharmony_ci        }
501141cc406Sopenharmony_ci        /* fall through */
502141cc406Sopenharmony_ci    default:
503141cc406Sopenharmony_ci    {
504141cc406Sopenharmony_ci        signed char min_diff;
505141cc406Sopenharmony_ci        u_char r_off, g_off, b_off;
506141cc406Sopenharmony_ci        signed char g = (pss->buf[INQUIRY_G2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_G2R_DIFF] & 0x7F) : pss->buf[INQUIRY_G2R_DIFF];
507141cc406Sopenharmony_ci        signed char b = (pss->buf[INQUIRY_B2R_DIFF] & 0x80) ? -(pss->buf[INQUIRY_B2R_DIFF] & 0x7F) : pss->buf[INQUIRY_B2R_DIFF];
508141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE, "%s: G2R_DIFF: %d\n", me, pss->buf[INQUIRY_G2R_DIFF]);
509141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE, "%s: B2R_DIFF: %d\n", me, pss->buf[INQUIRY_B2R_DIFF]);
510141cc406Sopenharmony_ci
511141cc406Sopenharmony_ci        min_diff = MIN (MIN (b, g), 0);
512141cc406Sopenharmony_ci        r_off = (u_char) (0 - min_diff);
513141cc406Sopenharmony_ci        g_off = (u_char) (g - min_diff);
514141cc406Sopenharmony_ci        b_off = (u_char) (b - min_diff);
515141cc406Sopenharmony_ci        pss->chroma_offset[R_CHAN] = r_off;
516141cc406Sopenharmony_ci        pss->chroma_offset[G_CHAN] = g_off;
517141cc406Sopenharmony_ci        pss->chroma_offset[B_CHAN] = b_off;
518141cc406Sopenharmony_ci        pss->chroma = MAX(MAX(r_off, g_off), b_off);
519141cc406Sopenharmony_ci        DBG (DL_DATA_TRACE,
520141cc406Sopenharmony_ci            "%s: Chroma offsets=%d; Red=%u, Green:=%u, Blue=%u\n",
521141cc406Sopenharmony_ci            me, pss->chroma,
522141cc406Sopenharmony_ci            pss->chroma_offset[R_CHAN],
523141cc406Sopenharmony_ci            pss->chroma_offset[G_CHAN],
524141cc406Sopenharmony_ci            pss->chroma_offset[B_CHAN]);
525141cc406Sopenharmony_ci        }
526141cc406Sopenharmony_ci        break;
527141cc406Sopenharmony_ci    }
528141cc406Sopenharmony_ci
529141cc406Sopenharmony_ci    pss->actual_res =
530141cc406Sopenharmony_ci        u_char_to_u_short (pss->buf + INQUIRY_OPT_RES);
531141cc406Sopenharmony_ci
532141cc406Sopenharmony_ci    pss->pixels_per_line =
533141cc406Sopenharmony_ci        u_char_to_u_short (pss->buf + INQUIRY_PIX_PER_LINE);
534141cc406Sopenharmony_ci    pss->bytes_per_line =
535141cc406Sopenharmony_ci        u_char_to_u_short (pss->buf + INQUIRY_BYTE_PER_LINE);
536141cc406Sopenharmony_ci    if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490))
537141cc406Sopenharmony_ci        pss->bytes_per_line += pss->buf[INQUIRY_BYTE_PER_LINE_MSB] << 16;
538141cc406Sopenharmony_ci    pss->lines =
539141cc406Sopenharmony_ci        u_char_to_u_short (pss->buf + INQUIRY_NUM_LINES) - pss->chroma;
540141cc406Sopenharmony_ci    /* effective buffer size must be a whole number of scan lines */
541141cc406Sopenharmony_ci    if (pss->lines)
542141cc406Sopenharmony_ci        pss->buf_sz = (pss->phys_buf_sz/pss->bytes_per_line)*pss->bytes_per_line;
543141cc406Sopenharmony_ci    else
544141cc406Sopenharmony_ci        pss->buf_sz = 0;
545141cc406Sopenharmony_ci    pss->bytes_remaining = pss->bytes_per_line * (pss->lines + pss->chroma);
546141cc406Sopenharmony_ci    pss->expected_read_bytes = 0;
547141cc406Sopenharmony_ci    pss->read_bytes = 0;
548141cc406Sopenharmony_ci    pss->hwst = pss->buf[INQUIRY_HWST];
549141cc406Sopenharmony_ci    if ((pss->pdev->bus == USB) && !(pss->hwst & 0x02))
550141cc406Sopenharmony_ci    {
551141cc406Sopenharmony_ci        pss->firmware_loaded = SANE_TRUE;
552141cc406Sopenharmony_ci    }
553141cc406Sopenharmony_ci    pss->hconfig = pss->buf[INQUIRY_HCFG];
554141cc406Sopenharmony_ci    switch (pss->pdev->model)
555141cc406Sopenharmony_ci    {
556141cc406Sopenharmony_ci    case PERFECTION1270:
557141cc406Sopenharmony_ci    case PERFECTION1670:
558141cc406Sopenharmony_ci    case PERFECTION2480:
559141cc406Sopenharmony_ci    case PERFECTION3490:
560141cc406Sopenharmony_ci    case PRISA5150:
561141cc406Sopenharmony_ci    case PRISA5000:
562141cc406Sopenharmony_ci        pss->bpp = 14;
563141cc406Sopenharmony_ci        break;
564141cc406Sopenharmony_ci    case STYLUS_CX1500:
565141cc406Sopenharmony_ci    case SCANWIT2720S:
566141cc406Sopenharmony_ci        pss->bpp = 12;
567141cc406Sopenharmony_ci        break;
568141cc406Sopenharmony_ci    default:
569141cc406Sopenharmony_ci        pss->bpp = 8;
570141cc406Sopenharmony_ci        if (pss->hconfig & HCFG_ADC)
571141cc406Sopenharmony_ci            pss->bpp = 10;
572141cc406Sopenharmony_ci        break;
573141cc406Sopenharmony_ci    }
574141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
575141cc406Sopenharmony_ci        "%s: hardware config = 0x%02x\n",
576141cc406Sopenharmony_ci        me,
577141cc406Sopenharmony_ci        pss->hconfig);
578141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
579141cc406Sopenharmony_ci        "%s: bits per pixel = %lu\n",
580141cc406Sopenharmony_ci        me,
581141cc406Sopenharmony_ci        (u_long) pss->bpp);
582141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
583141cc406Sopenharmony_ci        "%s: pixels per scan line = %lu\n",
584141cc406Sopenharmony_ci        me,
585141cc406Sopenharmony_ci        (u_long) pss->pixels_per_line);
586141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
587141cc406Sopenharmony_ci        "%s: bytes per scan line = %lu\n",
588141cc406Sopenharmony_ci        me,
589141cc406Sopenharmony_ci        (u_long) pss->bytes_per_line);
590141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
591141cc406Sopenharmony_ci        "%s: number of scan lines = %lu\n",
592141cc406Sopenharmony_ci        me,
593141cc406Sopenharmony_ci        (u_long) pss->lines);
594141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
595141cc406Sopenharmony_ci         "%s: effective buffer size = %lu bytes, %lu lines\n",
596141cc406Sopenharmony_ci         me,
597141cc406Sopenharmony_ci        (u_long) pss->buf_sz,
598141cc406Sopenharmony_ci        (u_long) (pss->lines ? pss->buf_sz / pss->lines : 0));
599141cc406Sopenharmony_ci    DBG (DL_DATA_TRACE,
600141cc406Sopenharmony_ci        "%s: expected total scan data: %lu bytes\n",
601141cc406Sopenharmony_ci        me,
602141cc406Sopenharmony_ci        (u_long) pss->bytes_remaining);
603141cc406Sopenharmony_ci
604141cc406Sopenharmony_ci    return status;
605141cc406Sopenharmony_ci}
606141cc406Sopenharmony_ci
607141cc406Sopenharmony_cistatic SANE_Status test_unit_ready (SnapScan_Scanner *pss)
608141cc406Sopenharmony_ci{
609141cc406Sopenharmony_ci    static const char *me = "test_unit_ready";
610141cc406Sopenharmony_ci    char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
611141cc406Sopenharmony_ci    SANE_Status status;
612141cc406Sopenharmony_ci
613141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
614141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
615141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
616141cc406Sopenharmony_ci    return status;
617141cc406Sopenharmony_ci}
618141cc406Sopenharmony_ci
619141cc406Sopenharmony_cistatic void reserve_unit (SnapScan_Scanner *pss)
620141cc406Sopenharmony_ci{
621141cc406Sopenharmony_ci    static const char *me = "reserve_unit";
622141cc406Sopenharmony_ci    char cmd[] = {RESERVE_UNIT, 0, 0, 0, 0, 0};
623141cc406Sopenharmony_ci    SANE_Status status;
624141cc406Sopenharmony_ci
625141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
626141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
627141cc406Sopenharmony_ci    if (status != SANE_STATUS_GOOD)
628141cc406Sopenharmony_ci    {
629141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR,
630141cc406Sopenharmony_ci            "%s: scsi command error: %s\n",
631141cc406Sopenharmony_ci            me,
632141cc406Sopenharmony_ci            sane_strstatus (status));
633141cc406Sopenharmony_ci    }
634141cc406Sopenharmony_ci}
635141cc406Sopenharmony_ci
636141cc406Sopenharmony_cistatic void release_unit (SnapScan_Scanner *pss)
637141cc406Sopenharmony_ci{
638141cc406Sopenharmony_ci    static const char *me = "release_unit";
639141cc406Sopenharmony_ci    char cmd[] = {RELEASE_UNIT, 0, 0, 0, 0, 0};
640141cc406Sopenharmony_ci    SANE_Status status;
641141cc406Sopenharmony_ci
642141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
643141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
644141cc406Sopenharmony_ci    if (status != SANE_STATUS_GOOD)
645141cc406Sopenharmony_ci    {
646141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR,
647141cc406Sopenharmony_ci            "%s: scsi command error: %s\n",
648141cc406Sopenharmony_ci            me, sane_strstatus (status));
649141cc406Sopenharmony_ci    }
650141cc406Sopenharmony_ci}
651141cc406Sopenharmony_ci
652141cc406Sopenharmony_ci#define SEND_LENGTH 10
653141cc406Sopenharmony_ci#define DTC_HALFTONE 0x02
654141cc406Sopenharmony_ci#define DTC_GAMMA 0x03
655141cc406Sopenharmony_ci#define DTC_GAMMA2 0x04
656141cc406Sopenharmony_ci#define DTC_SPEED 0x81
657141cc406Sopenharmony_ci#define DTC_CALIBRATION 0x82
658141cc406Sopenharmony_ci#define DTC_CALIBRATION_BLACK 0x89
659141cc406Sopenharmony_ci#define DTCQ_HALFTONE_BW8 0x00
660141cc406Sopenharmony_ci#define DTCQ_HALFTONE_COLOR8 0x01
661141cc406Sopenharmony_ci#define DTCQ_HALFTONE_BW16 0x80
662141cc406Sopenharmony_ci#define DTCQ_HALFTONE_COLOR16 0x81
663141cc406Sopenharmony_ci#define DTCQ_GAMMA_GRAY8 0x00
664141cc406Sopenharmony_ci#define DTCQ_GAMMA_RED8 0x01
665141cc406Sopenharmony_ci#define DTCQ_GAMMA_GREEN8 0x02
666141cc406Sopenharmony_ci#define DTCQ_GAMMA_BLUE8 0x03
667141cc406Sopenharmony_ci#define DTCQ_GAMMA_GRAY10 0x80
668141cc406Sopenharmony_ci#define DTCQ_GAMMA_RED10 0x81
669141cc406Sopenharmony_ci#define DTCQ_GAMMA_GREEN10 0x82
670141cc406Sopenharmony_ci#define DTCQ_GAMMA_BLUE10 0x83
671141cc406Sopenharmony_ci#define DTCQ_GAMMA_GRAY12 0x90
672141cc406Sopenharmony_ci#define DTCQ_GAMMA_RED12 0x91
673141cc406Sopenharmony_ci#define DTCQ_GAMMA_GREEN12 0x92
674141cc406Sopenharmony_ci#define DTCQ_GAMMA_BLUE12 0x93
675141cc406Sopenharmony_ci#define DTCQ_GAMMA_GRAY14 0x95 /* ? */
676141cc406Sopenharmony_ci#define DTCQ_GAMMA_RED14 0x96
677141cc406Sopenharmony_ci#define DTCQ_GAMMA_GREEN14 0x97
678141cc406Sopenharmony_ci#define DTCQ_GAMMA_BLUE14 0x98
679141cc406Sopenharmony_ci#define DTCQ_GAMMA_GRAY12_16BIT 0xa0
680141cc406Sopenharmony_ci#define DTCQ_GAMMA_RED12_16BIT 0xa1
681141cc406Sopenharmony_ci#define DTCQ_GAMMA_GREEN12_16BIT 0xa2
682141cc406Sopenharmony_ci#define DTCQ_GAMMA_BLUE12_16BIT 0xa3
683141cc406Sopenharmony_ci#define DTCQ_GAMMA_GRAY14_16BIT 0xa5 /* ? */
684141cc406Sopenharmony_ci#define DTCQ_GAMMA_RED14_16BIT 0xa6
685141cc406Sopenharmony_ci#define DTCQ_GAMMA_GREEN14_16BIT 0xa7
686141cc406Sopenharmony_ci#define DTCQ_GAMMA_BLUE14_16BIT 0xa8
687141cc406Sopenharmony_ci
688141cc406Sopenharmony_cistatic SANE_Status send (SnapScan_Scanner *pss, u_char dtc, u_char dtcq)
689141cc406Sopenharmony_ci{
690141cc406Sopenharmony_ci    static char me[] = "send";
691141cc406Sopenharmony_ci    SANE_Status status;
692141cc406Sopenharmony_ci    u_short tl;            /* transfer length */
693141cc406Sopenharmony_ci
694141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
695141cc406Sopenharmony_ci
696141cc406Sopenharmony_ci    zero_buf (pss->buf, SEND_LENGTH);
697141cc406Sopenharmony_ci
698141cc406Sopenharmony_ci    switch (dtc)
699141cc406Sopenharmony_ci    {
700141cc406Sopenharmony_ci    case DTC_HALFTONE:        /* halftone mask */
701141cc406Sopenharmony_ci        switch (dtcq)
702141cc406Sopenharmony_ci        {
703141cc406Sopenharmony_ci        case DTCQ_HALFTONE_BW8:
704141cc406Sopenharmony_ci            tl = 64;        /* bw 8x8 table */
705141cc406Sopenharmony_ci            break;
706141cc406Sopenharmony_ci        case DTCQ_HALFTONE_COLOR8:
707141cc406Sopenharmony_ci            tl = 3 * 64;    /* rgb 8x8 tables */
708141cc406Sopenharmony_ci            break;
709141cc406Sopenharmony_ci        case DTCQ_HALFTONE_BW16:
710141cc406Sopenharmony_ci            tl = 256;        /* bw 16x16 table */
711141cc406Sopenharmony_ci            break;
712141cc406Sopenharmony_ci        case DTCQ_HALFTONE_COLOR16:
713141cc406Sopenharmony_ci            tl = 3 * 256;    /* rgb 16x16 tables */
714141cc406Sopenharmony_ci            break;
715141cc406Sopenharmony_ci        default:
716141cc406Sopenharmony_ci            DBG (DL_MAJOR_ERROR, "%s: bad halftone data type qualifier 0x%x\n",
717141cc406Sopenharmony_ci                 me, dtcq);
718141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
719141cc406Sopenharmony_ci        }
720141cc406Sopenharmony_ci        break;
721141cc406Sopenharmony_ci    case DTC_GAMMA:        /* gamma function */
722141cc406Sopenharmony_ci    case DTC_GAMMA2:
723141cc406Sopenharmony_ci        switch (dtcq)
724141cc406Sopenharmony_ci        {
725141cc406Sopenharmony_ci        case DTCQ_GAMMA_GRAY8:    /* 8-bit tables */
726141cc406Sopenharmony_ci        case DTCQ_GAMMA_RED8:
727141cc406Sopenharmony_ci        case DTCQ_GAMMA_GREEN8:
728141cc406Sopenharmony_ci        case DTCQ_GAMMA_BLUE8:
729141cc406Sopenharmony_ci            tl = 256;
730141cc406Sopenharmony_ci            break;
731141cc406Sopenharmony_ci        case DTCQ_GAMMA_GRAY10:    /* 10-bit tables */
732141cc406Sopenharmony_ci        case DTCQ_GAMMA_RED10:
733141cc406Sopenharmony_ci        case DTCQ_GAMMA_GREEN10:
734141cc406Sopenharmony_ci        case DTCQ_GAMMA_BLUE10:
735141cc406Sopenharmony_ci            tl = 1024;
736141cc406Sopenharmony_ci            break;
737141cc406Sopenharmony_ci        case DTCQ_GAMMA_GRAY12:    /* 12-bit tables */
738141cc406Sopenharmony_ci        case DTCQ_GAMMA_RED12:
739141cc406Sopenharmony_ci        case DTCQ_GAMMA_GREEN12:
740141cc406Sopenharmony_ci        case DTCQ_GAMMA_BLUE12:
741141cc406Sopenharmony_ci            tl = 4096;
742141cc406Sopenharmony_ci            break;
743141cc406Sopenharmony_ci        case DTCQ_GAMMA_GRAY12_16BIT:    /* 12-bit tables with 16 bit data */
744141cc406Sopenharmony_ci        case DTCQ_GAMMA_RED12_16BIT:
745141cc406Sopenharmony_ci        case DTCQ_GAMMA_GREEN12_16BIT:
746141cc406Sopenharmony_ci        case DTCQ_GAMMA_BLUE12_16BIT:
747141cc406Sopenharmony_ci            tl = 8192;
748141cc406Sopenharmony_ci            break;
749141cc406Sopenharmony_ci        case DTCQ_GAMMA_GRAY14:    /* 14-bit tables */
750141cc406Sopenharmony_ci        case DTCQ_GAMMA_RED14:
751141cc406Sopenharmony_ci        case DTCQ_GAMMA_GREEN14:
752141cc406Sopenharmony_ci        case DTCQ_GAMMA_BLUE14:
753141cc406Sopenharmony_ci            tl = 16384;
754141cc406Sopenharmony_ci            break;
755141cc406Sopenharmony_ci        case DTCQ_GAMMA_GRAY14_16BIT:    /* 14-bit tables with 16 bit data */
756141cc406Sopenharmony_ci        case DTCQ_GAMMA_RED14_16BIT:
757141cc406Sopenharmony_ci        case DTCQ_GAMMA_GREEN14_16BIT:
758141cc406Sopenharmony_ci        case DTCQ_GAMMA_BLUE14_16BIT:
759141cc406Sopenharmony_ci            tl = 32768;
760141cc406Sopenharmony_ci            break;
761141cc406Sopenharmony_ci        default:
762141cc406Sopenharmony_ci            DBG (DL_MAJOR_ERROR, "%s: bad gamma data type qualifier 0x%x\n",
763141cc406Sopenharmony_ci                 me, dtcq);
764141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
765141cc406Sopenharmony_ci        }
766141cc406Sopenharmony_ci        break;
767141cc406Sopenharmony_ci    case DTC_SPEED:        /* static transfer speed */
768141cc406Sopenharmony_ci        tl = 2;
769141cc406Sopenharmony_ci        break;
770141cc406Sopenharmony_ci    case DTC_CALIBRATION:
771141cc406Sopenharmony_ci        tl = calibration_line_length(pss);
772141cc406Sopenharmony_ci        break;
773141cc406Sopenharmony_ci    default:
774141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR, "%s: unsupported data type code 0x%x\n",
775141cc406Sopenharmony_ci             me, (unsigned) dtc);
776141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
777141cc406Sopenharmony_ci    }
778141cc406Sopenharmony_ci
779141cc406Sopenharmony_ci    pss->buf[0] = SEND;
780141cc406Sopenharmony_ci    pss->buf[2] = dtc;
781141cc406Sopenharmony_ci    pss->buf[5] = dtcq;
782141cc406Sopenharmony_ci    pss->buf[7] = (tl >> 8) & 0xff;
783141cc406Sopenharmony_ci    pss->buf[8] = tl & 0xff;
784141cc406Sopenharmony_ci
785141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
786141cc406Sopenharmony_ci                             NULL, NULL);
787141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
788141cc406Sopenharmony_ci    return status;
789141cc406Sopenharmony_ci}
790141cc406Sopenharmony_ci
791141cc406Sopenharmony_ci#include "snapscan-data.c"
792141cc406Sopenharmony_cistatic SANE_Status send_calibration_5150(SnapScan_Scanner *pss)
793141cc406Sopenharmony_ci{
794141cc406Sopenharmony_ci    static const int length = sizeof(calibration_data_5150);
795141cc406Sopenharmony_ci    SANE_Byte* buf = malloc (length + SEND_LENGTH);
796141cc406Sopenharmony_ci    SANE_Status status;
797141cc406Sopenharmony_ci    zero_buf (buf, SEND_LENGTH);
798141cc406Sopenharmony_ci    *buf = SEND;
799141cc406Sopenharmony_ci    *(buf + 2) = DTC_CALIBRATION;
800141cc406Sopenharmony_ci    *(buf + 6) = (length >> 16) & 0xff;
801141cc406Sopenharmony_ci    *(buf + 7) = (length >> 8) & 0xff;
802141cc406Sopenharmony_ci    *(buf + 8) = length & 0xff;
803141cc406Sopenharmony_ci    memcpy(buf + SEND_LENGTH, calibration_data_5150, length);
804141cc406Sopenharmony_ci    status = snapscan_cmd (
805141cc406Sopenharmony_ci      pss->pdev->bus, pss->fd, buf, SEND_LENGTH + length, NULL, NULL);
806141cc406Sopenharmony_ci    free (buf);
807141cc406Sopenharmony_ci    return status;
808141cc406Sopenharmony_ci}
809141cc406Sopenharmony_ci
810141cc406Sopenharmony_ci#define SET_WINDOW_LEN             10
811141cc406Sopenharmony_ci#define SET_WINDOW_HEADER         10    /* header starts */
812141cc406Sopenharmony_ci#define SET_WINDOW_HEADER_LEN         8
813141cc406Sopenharmony_ci#define SET_WINDOW_DESC         18    /* window descriptor starts */
814141cc406Sopenharmony_ci#define SET_WINDOW_DESC_LEN         48
815141cc406Sopenharmony_ci#define SET_WINDOW_TRANSFER_LEN     56
816141cc406Sopenharmony_ci#define SET_WINDOW_TOTAL_LEN         66
817141cc406Sopenharmony_ci#define SET_WINDOW_RET_LEN           0    /* no returned data */
818141cc406Sopenharmony_ci
819141cc406Sopenharmony_ci#define SET_WINDOW_P_TRANSFER_LEN     6
820141cc406Sopenharmony_ci#define SET_WINDOW_P_DESC_LEN         6
821141cc406Sopenharmony_ci
822141cc406Sopenharmony_ci#define SET_WINDOW_P_WIN_ID         0
823141cc406Sopenharmony_ci#define SET_WINDOW_P_XRES           2
824141cc406Sopenharmony_ci#define SET_WINDOW_P_YRES           4
825141cc406Sopenharmony_ci#define SET_WINDOW_P_TLX            6
826141cc406Sopenharmony_ci#define SET_WINDOW_P_TLY            10
827141cc406Sopenharmony_ci#define SET_WINDOW_P_WIDTH          14
828141cc406Sopenharmony_ci#define SET_WINDOW_P_LENGTH         18
829141cc406Sopenharmony_ci#define SET_WINDOW_P_BRIGHTNESS       22
830141cc406Sopenharmony_ci#define SET_WINDOW_P_THRESHOLD        23
831141cc406Sopenharmony_ci#define SET_WINDOW_P_CONTRAST         24
832141cc406Sopenharmony_ci#define SET_WINDOW_P_COMPOSITION      25
833141cc406Sopenharmony_ci#define SET_WINDOW_P_BITS_PER_PIX     26
834141cc406Sopenharmony_ci#define SET_WINDOW_P_HALFTONE_PATTERN      27
835141cc406Sopenharmony_ci#define SET_WINDOW_P_PADDING_TYPE          29
836141cc406Sopenharmony_ci#define SET_WINDOW_P_BIT_ORDERING          30
837141cc406Sopenharmony_ci#define SET_WINDOW_P_COMPRESSION_TYPE      32
838141cc406Sopenharmony_ci#define SET_WINDOW_P_COMPRESSION_ARG       33
839141cc406Sopenharmony_ci#define SET_WINDOW_P_HALFTONE_FLAG         35
840141cc406Sopenharmony_ci#define SET_WINDOW_P_DEBUG_MODE            40
841141cc406Sopenharmony_ci#define SET_WINDOW_P_GAMMA_NO              41
842141cc406Sopenharmony_ci#define SET_WINDOW_P_OPERATION_MODE        42
843141cc406Sopenharmony_ci#define SET_WINDOW_P_RED_UNDER_COLOR       43
844141cc406Sopenharmony_ci#define SET_WINDOW_P_BLUE_UNDER_COLOR      45
845141cc406Sopenharmony_ci#define SET_WINDOW_P_GREEN_UNDER_COLOR     44
846141cc406Sopenharmony_ci
847141cc406Sopenharmony_cistatic SANE_Status prepare_set_window (SnapScan_Scanner *pss)
848141cc406Sopenharmony_ci{
849141cc406Sopenharmony_ci    static const char *me = "prepare_set_window";
850141cc406Sopenharmony_ci    u_char *pc;
851141cc406Sopenharmony_ci
852141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
853141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
854141cc406Sopenharmony_ci
855141cc406Sopenharmony_ci    /* basic command */
856141cc406Sopenharmony_ci    pc = pss->cmd;
857141cc406Sopenharmony_ci    pc[0] = SET_WINDOW;
858141cc406Sopenharmony_ci    u_int_to_u_char3p ((u_int) SET_WINDOW_TRANSFER_LEN,
859141cc406Sopenharmony_ci                       pc + SET_WINDOW_P_TRANSFER_LEN);
860141cc406Sopenharmony_ci
861141cc406Sopenharmony_ci    /* header; we support only one window */
862141cc406Sopenharmony_ci    pc += SET_WINDOW_LEN;
863141cc406Sopenharmony_ci    u_short_to_u_charp (SET_WINDOW_DESC_LEN, pc + SET_WINDOW_P_DESC_LEN);
864141cc406Sopenharmony_ci
865141cc406Sopenharmony_ci    /* the sole window descriptor */
866141cc406Sopenharmony_ci    pc += SET_WINDOW_HEADER_LEN;
867141cc406Sopenharmony_ci    pc[SET_WINDOW_P_WIN_ID] = 0;
868141cc406Sopenharmony_ci    u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_XRES);
869141cc406Sopenharmony_ci    u_short_to_u_charp (pss->res, pc + SET_WINDOW_P_YRES);
870141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s Resolution: %d\n", me, pss->res);
871141cc406Sopenharmony_ci
872141cc406Sopenharmony_ci    pc[SET_WINDOW_P_BRIGHTNESS] = 128;
873141cc406Sopenharmony_ci    pc[SET_WINDOW_P_THRESHOLD] =
874141cc406Sopenharmony_ci        (u_char) (255.0*(pss->threshold / 100.0));
875141cc406Sopenharmony_ci    pc[SET_WINDOW_P_CONTRAST] = 128;
876141cc406Sopenharmony_ci
877141cc406Sopenharmony_ci    {
878141cc406Sopenharmony_ci        SnapScan_Mode mode = pss->mode;
879141cc406Sopenharmony_ci        pss->bpp_scan = pss->val[OPT_BIT_DEPTH].w;
880141cc406Sopenharmony_ci
881141cc406Sopenharmony_ci        if (pss->preview)
882141cc406Sopenharmony_ci        {
883141cc406Sopenharmony_ci            mode = pss->preview_mode;
884141cc406Sopenharmony_ci            if (pss->pdev->model != SCANWIT2720S)
885141cc406Sopenharmony_ci                pss->bpp_scan = 8;
886141cc406Sopenharmony_ci        }
887141cc406Sopenharmony_ci
888141cc406Sopenharmony_ci        DBG (DL_MINOR_INFO, "%s Mode: %d\n", me, mode);
889141cc406Sopenharmony_ci        switch (mode)
890141cc406Sopenharmony_ci        {
891141cc406Sopenharmony_ci        case MD_COLOUR:
892141cc406Sopenharmony_ci            pc[SET_WINDOW_P_COMPOSITION] = 0x05;    /* multi-level RGB */
893141cc406Sopenharmony_ci            break;
894141cc406Sopenharmony_ci        case MD_BILEVELCOLOUR:
895141cc406Sopenharmony_ci            if (pss->halftone)
896141cc406Sopenharmony_ci                pc[SET_WINDOW_P_COMPOSITION] = 0x04;    /* halftone RGB */
897141cc406Sopenharmony_ci            else
898141cc406Sopenharmony_ci                pc[SET_WINDOW_P_COMPOSITION] = 0x03;    /* bi-level RGB */
899141cc406Sopenharmony_ci            pss->bpp_scan = 1;
900141cc406Sopenharmony_ci            break;
901141cc406Sopenharmony_ci        case MD_GREYSCALE:
902141cc406Sopenharmony_ci            pc[SET_WINDOW_P_COMPOSITION] = 0x02;    /* grayscale */
903141cc406Sopenharmony_ci            break;
904141cc406Sopenharmony_ci        case MD_LINEART:
905141cc406Sopenharmony_ci            if (pss->halftone)
906141cc406Sopenharmony_ci                pc[SET_WINDOW_P_COMPOSITION] = 0x01;    /* b&w halftone */
907141cc406Sopenharmony_ci            else
908141cc406Sopenharmony_ci                pc[SET_WINDOW_P_COMPOSITION] = 0x00;    /* b&w (lineart) */
909141cc406Sopenharmony_ci            pss->bpp_scan = 1;
910141cc406Sopenharmony_ci            break;
911141cc406Sopenharmony_ci        default:
912141cc406Sopenharmony_ci            break;
913141cc406Sopenharmony_ci        }
914141cc406Sopenharmony_ci
915141cc406Sopenharmony_ci        pc[SET_WINDOW_P_BITS_PER_PIX] = pss->bpp_scan;
916141cc406Sopenharmony_ci        DBG (DL_INFO, "%s: bits-per-pixel set to %d\n", me, (int) pss->bpp_scan);
917141cc406Sopenharmony_ci    }
918141cc406Sopenharmony_ci    /* the RIF bit is the high bit of the padding type */
919141cc406Sopenharmony_ci    pc[SET_WINDOW_P_PADDING_TYPE] = 0x00 /*| (pss->negative ? 0x00 : 0x80) */ ;
920141cc406Sopenharmony_ci    pc[SET_WINDOW_P_HALFTONE_PATTERN] = 0;
921141cc406Sopenharmony_ci    pc[SET_WINDOW_P_HALFTONE_FLAG] = 0x80;    /* always set; image
922141cc406Sopenharmony_ci                                            composition
923141cc406Sopenharmony_ci                                            determines whether
924141cc406Sopenharmony_ci                                            halftone is
925141cc406Sopenharmony_ci                                            actually used */
926141cc406Sopenharmony_ci
927141cc406Sopenharmony_ci    u_short_to_u_charp (0x0000, pc + SET_WINDOW_P_BIT_ORDERING);    /* used? */
928141cc406Sopenharmony_ci    pc[SET_WINDOW_P_COMPRESSION_TYPE] = 0;    /* none */
929141cc406Sopenharmony_ci    pc[SET_WINDOW_P_COMPRESSION_ARG] = 0;    /* none applicable */
930141cc406Sopenharmony_ci    if(pss->pdev->model != ACER300F
931141cc406Sopenharmony_ci       &&
932141cc406Sopenharmony_ci       pss->pdev->model != SNAPSCAN310
933141cc406Sopenharmony_ci       &&
934141cc406Sopenharmony_ci       pss->pdev->model != PRISA310
935141cc406Sopenharmony_ci       &&
936141cc406Sopenharmony_ci       pss->pdev->model != PRISA610
937141cc406Sopenharmony_ci    ) {
938141cc406Sopenharmony_ci        pc[SET_WINDOW_P_DEBUG_MODE] = 2;        /* use full 128k buffer */
939141cc406Sopenharmony_ci        if (pss->mode != MD_LINEART)
940141cc406Sopenharmony_ci        {
941141cc406Sopenharmony_ci            pc[SET_WINDOW_P_GAMMA_NO] = 0x01;   /* downloaded gamma table */
942141cc406Sopenharmony_ci        }
943141cc406Sopenharmony_ci    }
944141cc406Sopenharmony_ci
945141cc406Sopenharmony_ci    pc[SET_WINDOW_P_RED_UNDER_COLOR] = 0xff;    /* defaults */
946141cc406Sopenharmony_ci    pc[SET_WINDOW_P_BLUE_UNDER_COLOR] = 0xff;
947141cc406Sopenharmony_ci    pc[SET_WINDOW_P_GREEN_UNDER_COLOR] = 0xff;
948141cc406Sopenharmony_ci
949141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
950141cc406Sopenharmony_ci}
951141cc406Sopenharmony_ci
952141cc406Sopenharmony_cistatic SANE_Status set_window (SnapScan_Scanner *pss)
953141cc406Sopenharmony_ci{
954141cc406Sopenharmony_ci    static const char *me = "set_window";
955141cc406Sopenharmony_ci    SANE_Status status;
956141cc406Sopenharmony_ci    unsigned char source;
957141cc406Sopenharmony_ci    u_char *pc;
958141cc406Sopenharmony_ci    int pos_factor;
959141cc406Sopenharmony_ci
960141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
961141cc406Sopenharmony_ci    status = prepare_set_window(pss);
962141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "prepare_set_window");
963141cc406Sopenharmony_ci
964141cc406Sopenharmony_ci    pc = pss->cmd;
965141cc406Sopenharmony_ci
966141cc406Sopenharmony_ci    /* header; we support only one window */
967141cc406Sopenharmony_ci    pc += SET_WINDOW_LEN;
968141cc406Sopenharmony_ci
969141cc406Sopenharmony_ci    /* the sole window descriptor */
970141cc406Sopenharmony_ci    pc += SET_WINDOW_HEADER_LEN;
971141cc406Sopenharmony_ci
972141cc406Sopenharmony_ci    switch (pss->pdev->model)
973141cc406Sopenharmony_ci    {
974141cc406Sopenharmony_ci        case PRISA5000:
975141cc406Sopenharmony_ci        case PRISA5000E:
976141cc406Sopenharmony_ci        case PRISA5150:
977141cc406Sopenharmony_ci            pos_factor = (pss->res > 600) ?  1200 : 600;
978141cc406Sopenharmony_ci            break;
979141cc406Sopenharmony_ci        case PERFECTION1270:
980141cc406Sopenharmony_ci        case PERFECTION1670:
981141cc406Sopenharmony_ci            pos_factor = (pss->res > 800) ?  1600 : 800;
982141cc406Sopenharmony_ci            break;
983141cc406Sopenharmony_ci        case PERFECTION2480:
984141cc406Sopenharmony_ci            pos_factor = (pss->res > 1200) ?  2400 : 1200;
985141cc406Sopenharmony_ci            break;
986141cc406Sopenharmony_ci        case PERFECTION3490:
987141cc406Sopenharmony_ci            pos_factor = (pss->res > 1600) ?  3200 : 1600;
988141cc406Sopenharmony_ci            break;
989141cc406Sopenharmony_ci        default:
990141cc406Sopenharmony_ci            pos_factor = pss->actual_res;
991141cc406Sopenharmony_ci            break;
992141cc406Sopenharmony_ci    }
993141cc406Sopenharmony_ci    /* it's an ugly sound if the scanner drives against the rear
994141cc406Sopenharmony_ci       wall, and with changing max values we better be sure */
995141cc406Sopenharmony_ci    check_range(&(pss->brx), pss->pdev->x_range);
996141cc406Sopenharmony_ci    check_range(&(pss->bry), pss->pdev->y_range);
997141cc406Sopenharmony_ci    {
998141cc406Sopenharmony_ci        int tlxp =
999141cc406Sopenharmony_ci            (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tlx));
1000141cc406Sopenharmony_ci        int tlyp =
1001141cc406Sopenharmony_ci            (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->tly));
1002141cc406Sopenharmony_ci        int brxp =
1003141cc406Sopenharmony_ci            (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->brx));
1004141cc406Sopenharmony_ci        int bryp =
1005141cc406Sopenharmony_ci            (int) (pos_factor*IN_PER_MM*SANE_UNFIX(pss->bry));
1006141cc406Sopenharmony_ci
1007141cc406Sopenharmony_ci        /* Check for brx > tlx and bry > tly */
1008141cc406Sopenharmony_ci        if (brxp <= tlxp) {
1009141cc406Sopenharmony_ci            tlxp = MAX (0, brxp - 75);
1010141cc406Sopenharmony_ci        }
1011141cc406Sopenharmony_ci        if (bryp <= tlyp) {
1012141cc406Sopenharmony_ci            tlyp = MAX (0, bryp - 75);
1013141cc406Sopenharmony_ci        }
1014141cc406Sopenharmony_ci
1015141cc406Sopenharmony_ci        u_int_to_u_char4p (tlxp, pc + SET_WINDOW_P_TLX);
1016141cc406Sopenharmony_ci        u_int_to_u_char4p (tlyp, pc + SET_WINDOW_P_TLY);
1017141cc406Sopenharmony_ci        u_int_to_u_char4p (MAX (((unsigned) (brxp - tlxp)), 75),
1018141cc406Sopenharmony_ci                           pc + SET_WINDOW_P_WIDTH);
1019141cc406Sopenharmony_ci        u_int_to_u_char4p (MAX (((unsigned) (bryp - tlyp)), 75),
1020141cc406Sopenharmony_ci                           pc + SET_WINDOW_P_LENGTH);
1021141cc406Sopenharmony_ci        DBG (DL_INFO, "%s Width:  %d\n", me, brxp-tlxp);
1022141cc406Sopenharmony_ci        DBG (DL_INFO, "%s Length: %d\n", me, bryp-tlyp);
1023141cc406Sopenharmony_ci    }
1024141cc406Sopenharmony_ci
1025141cc406Sopenharmony_ci    source = 0x0;
1026141cc406Sopenharmony_ci    if (pss->preview) {
1027141cc406Sopenharmony_ci        source |= 0x80; /* no high quality in preview */
1028141cc406Sopenharmony_ci    }
1029141cc406Sopenharmony_ci    else {
1030141cc406Sopenharmony_ci        source |= 0x40; /* no preview */
1031141cc406Sopenharmony_ci    }
1032141cc406Sopenharmony_ci    if (!pss->highquality) {
1033141cc406Sopenharmony_ci        source |= 0x80; /* no high quality */
1034141cc406Sopenharmony_ci    }
1035141cc406Sopenharmony_ci    if ((pss->pdev->model == PERFECTION2480) || (pss->pdev->model == PERFECTION3490)) {
1036141cc406Sopenharmony_ci        source |= 0x40; /* 2480/3490 always need no_preview bit */
1037141cc406Sopenharmony_ci    }
1038141cc406Sopenharmony_ci    if (pss->source == SRC_TPO) {
1039141cc406Sopenharmony_ci        source |= 0x08;
1040141cc406Sopenharmony_ci    }
1041141cc406Sopenharmony_ci    if (pss->source == SRC_ADF) {
1042141cc406Sopenharmony_ci        source |= 0x10;
1043141cc406Sopenharmony_ci    }
1044141cc406Sopenharmony_ci    pc[SET_WINDOW_P_OPERATION_MODE] = source;
1045141cc406Sopenharmony_ci    DBG (DL_MINOR_INFO, "%s: operation mode set to 0x%02x\n", me, (int) source);
1046141cc406Sopenharmony_ci
1047141cc406Sopenharmony_ci    do {
1048141cc406Sopenharmony_ci        status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
1049141cc406Sopenharmony_ci                  SET_WINDOW_TOTAL_LEN, NULL, NULL);
1050141cc406Sopenharmony_ci        if (status == SANE_STATUS_DEVICE_BUSY) {
1051141cc406Sopenharmony_ci            DBG (DL_MINOR_INFO, "%s: waiting for scanner to warm up\n", me);
1052141cc406Sopenharmony_ci            wait_scanner_ready (pss);
1053141cc406Sopenharmony_ci        }
1054141cc406Sopenharmony_ci    } while (status == SANE_STATUS_DEVICE_BUSY);
1055141cc406Sopenharmony_ci
1056141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1057141cc406Sopenharmony_ci    return status;
1058141cc406Sopenharmony_ci}
1059141cc406Sopenharmony_ci
1060141cc406Sopenharmony_cistatic SANE_Status set_window_autofocus (SnapScan_Scanner *copy)
1061141cc406Sopenharmony_ci{
1062141cc406Sopenharmony_ci    static char me[] = "set_window_autofocus";
1063141cc406Sopenharmony_ci    SANE_Status status;
1064141cc406Sopenharmony_ci
1065141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s(%p)\n", me, (void*)copy);
1066141cc406Sopenharmony_ci
1067141cc406Sopenharmony_ci    copy->res = copy->actual_res;
1068141cc406Sopenharmony_ci    status = prepare_set_window (copy);
1069141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "prepare_set_window");
1070141cc406Sopenharmony_ci
1071141cc406Sopenharmony_ci    u_int_to_u_char4p (1700, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_TLY);
1072141cc406Sopenharmony_ci    /* fill in width & height */
1073141cc406Sopenharmony_ci    u_int_to_u_char4p (2550, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_WIDTH);
1074141cc406Sopenharmony_ci    u_int_to_u_char4p (128, copy->cmd + SET_WINDOW_DESC + SET_WINDOW_P_LENGTH);
1075141cc406Sopenharmony_ci
1076141cc406Sopenharmony_ci    copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_BITS_PER_PIX] = 12;
1077141cc406Sopenharmony_ci    copy->cmd[SET_WINDOW_DESC + SET_WINDOW_P_OPERATION_MODE] = 0x49; /* focusing mode */
1078141cc406Sopenharmony_ci    return snapscan_cmd (copy->pdev->bus, copy->fd, copy->cmd,
1079141cc406Sopenharmony_ci                SET_WINDOW_TOTAL_LEN, NULL, NULL);
1080141cc406Sopenharmony_ci}
1081141cc406Sopenharmony_ci
1082141cc406Sopenharmony_ci#define SET_FRAME_LEN	10
1083141cc406Sopenharmony_ci
1084141cc406Sopenharmony_cistatic SANE_Status set_frame (SnapScan_Scanner *pss, SANE_Byte frame_no)
1085141cc406Sopenharmony_ci{
1086141cc406Sopenharmony_ci    static const char *me = "set_frame";
1087141cc406Sopenharmony_ci    SANE_Status status;
1088141cc406Sopenharmony_ci
1089141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1090141cc406Sopenharmony_ci    DBG (DL_VERBOSE, "%s setting frame to %d\n", me, frame_no);
1091141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
1092141cc406Sopenharmony_ci    pss->cmd[0] = OBJECT_POSITION;
1093141cc406Sopenharmony_ci    pss->cmd[1] = 2;          /* Absolute position used here to select the frame */
1094141cc406Sopenharmony_ci    pss->cmd[4] = frame_no;
1095141cc406Sopenharmony_ci
1096141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
1097141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1098141cc406Sopenharmony_ci
1099141cc406Sopenharmony_ci    return status;
1100141cc406Sopenharmony_ci}
1101141cc406Sopenharmony_ci
1102141cc406Sopenharmony_cistatic SANE_Status set_focus (SnapScan_Scanner *pss, SANE_Int focus)
1103141cc406Sopenharmony_ci{
1104141cc406Sopenharmony_ci    static const char *me = "set_focus";
1105141cc406Sopenharmony_ci    SANE_Status status;
1106141cc406Sopenharmony_ci
1107141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s(%d)\n", me, focus);
1108141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
1109141cc406Sopenharmony_ci    pss->cmd[0] = OBJECT_POSITION;
1110141cc406Sopenharmony_ci    pss->cmd[1] = 4;          /* Rotate object but here it sets the focus point */
1111141cc406Sopenharmony_ci    pss->cmd[3] = (focus >> 8) & 0xFF;
1112141cc406Sopenharmony_ci    pss->cmd[4] = focus & 0xFF;
1113141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SET_FRAME_LEN, NULL, NULL);
1114141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1115141cc406Sopenharmony_ci    return status;
1116141cc406Sopenharmony_ci}
1117141cc406Sopenharmony_ci
1118141cc406Sopenharmony_cistatic SANE_Int get_8 (u_char *p)
1119141cc406Sopenharmony_ci{
1120141cc406Sopenharmony_ci    SANE_Int b;
1121141cc406Sopenharmony_ci    b = p[0] | (p[1] << 8);
1122141cc406Sopenharmony_ci    return b;
1123141cc406Sopenharmony_ci}
1124141cc406Sopenharmony_ci
1125141cc406Sopenharmony_cistatic double get_val (u_char *p, SANE_Int len, SANE_Int x)
1126141cc406Sopenharmony_ci{
1127141cc406Sopenharmony_ci	return get_8 (p + ((x + len) << 1)) / 255.0;
1128141cc406Sopenharmony_ci}
1129141cc406Sopenharmony_ci
1130141cc406Sopenharmony_cistatic double sum_pixel_differences (u_char *p, int len)
1131141cc406Sopenharmony_ci{
1132141cc406Sopenharmony_ci	double v, m, s;
1133141cc406Sopenharmony_ci	SANE_Int i;
1134141cc406Sopenharmony_ci
1135141cc406Sopenharmony_ci	s = 0;
1136141cc406Sopenharmony_ci	for (i = 0; i < len-1; i++) {
1137141cc406Sopenharmony_ci		v = get_val (p, len, i);
1138141cc406Sopenharmony_ci		m = get_val (p, len, i+1);
1139141cc406Sopenharmony_ci		s += fabs (v - m);
1140141cc406Sopenharmony_ci	}
1141141cc406Sopenharmony_ci	return s;
1142141cc406Sopenharmony_ci}
1143141cc406Sopenharmony_ci
1144141cc406Sopenharmony_cistatic SANE_Status scan (SnapScan_Scanner *pss)
1145141cc406Sopenharmony_ci{
1146141cc406Sopenharmony_ci    static const char *me = "scan";
1147141cc406Sopenharmony_ci    SANE_Status status;
1148141cc406Sopenharmony_ci
1149141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1150141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
1151141cc406Sopenharmony_ci    pss->cmd[0] = SCAN;
1152141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd, SCAN_LEN, NULL, NULL);
1153141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1154141cc406Sopenharmony_ci    return status;
1155141cc406Sopenharmony_ci}
1156141cc406Sopenharmony_ci
1157141cc406Sopenharmony_ci/* supported read operations */
1158141cc406Sopenharmony_ci
1159141cc406Sopenharmony_ci#define READ_IMAGE     0x00
1160141cc406Sopenharmony_ci#define READ_TRANSTIME 0x80
1161141cc406Sopenharmony_ci
1162141cc406Sopenharmony_ci/* number of bytes expected must be in pss->expected_read_bytes */
1163141cc406Sopenharmony_cistatic SANE_Status scsi_read (SnapScan_Scanner *pss, u_char read_type)
1164141cc406Sopenharmony_ci{
1165141cc406Sopenharmony_ci    static const char *me = "scsi_read";
1166141cc406Sopenharmony_ci    SANE_Status status;
1167141cc406Sopenharmony_ci
1168141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1169141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
1170141cc406Sopenharmony_ci    pss->cmd[0] = READ;
1171141cc406Sopenharmony_ci    pss->cmd[2] = read_type;
1172141cc406Sopenharmony_ci    if (read_type == READ_TRANSTIME && pss->pdev->model == PERFECTION2480)
1173141cc406Sopenharmony_ci        pss->cmd[5] = 1;
1174141cc406Sopenharmony_ci
1175141cc406Sopenharmony_ci    u_int_to_u_char3p (pss->expected_read_bytes, pss->cmd + 6);
1176141cc406Sopenharmony_ci
1177141cc406Sopenharmony_ci    pss->read_bytes = pss->expected_read_bytes;
1178141cc406Sopenharmony_ci
1179141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
1180141cc406Sopenharmony_ci                 READ_LEN, pss->buf, &pss->read_bytes);
1181141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1182141cc406Sopenharmony_ci    return status;
1183141cc406Sopenharmony_ci}
1184141cc406Sopenharmony_ci
1185141cc406Sopenharmony_cistatic SANE_Status get_focus (SnapScan_Scanner *pss)
1186141cc406Sopenharmony_ci{
1187141cc406Sopenharmony_ci    static const char *me = "get_focus";
1188141cc406Sopenharmony_ci    SANE_Int focus_point, max_focus_point;
1189141cc406Sopenharmony_ci    double sum, max;
1190141cc406Sopenharmony_ci    SANE_Status status;
1191141cc406Sopenharmony_ci    SnapScan_Scanner copy;
1192141cc406Sopenharmony_ci
1193141cc406Sopenharmony_ci    copy = *pss;
1194141cc406Sopenharmony_ci
1195141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1196141cc406Sopenharmony_ci    reserve_unit(&copy);
1197141cc406Sopenharmony_ci
1198141cc406Sopenharmony_ci    status = set_window_autofocus (&copy);
1199141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "set_window_autofocus");
1200141cc406Sopenharmony_ci
1201141cc406Sopenharmony_ci    status = inquiry (&copy);
1202141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "inquiry");
1203141cc406Sopenharmony_ci
1204141cc406Sopenharmony_ci    status = scan (&copy);
1205141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "scan");
1206141cc406Sopenharmony_ci
1207141cc406Sopenharmony_ci    status = set_frame (&copy, copy.frame_no);
1208141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "set_frame");
1209141cc406Sopenharmony_ci
1210141cc406Sopenharmony_ci    DBG (DL_VERBOSE, "%s: Expected number of bytes for each read %d\n", me, (int)copy.bytes_per_line);
1211141cc406Sopenharmony_ci    DBG (DL_VERBOSE, "%s: Expected number of pixels per line %d\n", me, (int)copy.pixels_per_line);
1212141cc406Sopenharmony_ci    max_focus_point = -1;
1213141cc406Sopenharmony_ci    max = -1;
1214141cc406Sopenharmony_ci    for (focus_point = 0; focus_point <= 0x300; focus_point += 6) {
1215141cc406Sopenharmony_ci        status = set_focus (&copy, focus_point);
1216141cc406Sopenharmony_ci        CHECK_STATUS (status, me, "set_focus");
1217141cc406Sopenharmony_ci
1218141cc406Sopenharmony_ci        copy.expected_read_bytes = copy.bytes_per_line;
1219141cc406Sopenharmony_ci        status = scsi_read (&copy, READ_IMAGE);
1220141cc406Sopenharmony_ci        CHECK_STATUS (status, me, "scsi_read");
1221141cc406Sopenharmony_ci
1222141cc406Sopenharmony_ci        sum = sum_pixel_differences (copy.buf, copy.pixels_per_line);
1223141cc406Sopenharmony_ci
1224141cc406Sopenharmony_ci        if (sum > max) {
1225141cc406Sopenharmony_ci            max = sum;
1226141cc406Sopenharmony_ci            max_focus_point = focus_point;
1227141cc406Sopenharmony_ci        }
1228141cc406Sopenharmony_ci    }
1229141cc406Sopenharmony_ci    pss->focus = max_focus_point;
1230141cc406Sopenharmony_ci    DBG (DL_VERBOSE, "%s: Focus point found to be at 0x%x\n", me, max_focus_point);
1231141cc406Sopenharmony_ci    release_unit (&copy);
1232141cc406Sopenharmony_ci
1233141cc406Sopenharmony_ci    wait_scanner_ready (&copy);
1234141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "wait_scanner_ready");
1235141cc406Sopenharmony_ci
1236141cc406Sopenharmony_ci    return status;
1237141cc406Sopenharmony_ci}
1238141cc406Sopenharmony_ci
1239141cc406Sopenharmony_ci/*
1240141cc406Sopenharmony_cistatic SANE_Status request_sense (SnapScan_Scanner *pss)
1241141cc406Sopenharmony_ci{
1242141cc406Sopenharmony_ci    static const char *me = "request_sense";
1243141cc406Sopenharmony_ci    size_t read_bytes = 0;
1244141cc406Sopenharmony_ci    u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, 20, 0};
1245141cc406Sopenharmony_ci    u_char data[20];
1246141cc406Sopenharmony_ci    SANE_Status status;
1247141cc406Sopenharmony_ci
1248141cc406Sopenharmony_ci    read_bytes = 20;
1249141cc406Sopenharmony_ci
1250141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1251141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd),
1252141cc406Sopenharmony_ci               data, &read_bytes);
1253141cc406Sopenharmony_ci    if (status != SANE_STATUS_GOOD)
1254141cc406Sopenharmony_ci    {
1255141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR, "%s: scsi command error: %s\n",
1256141cc406Sopenharmony_ci             me, sane_strstatus (status));
1257141cc406Sopenharmony_ci    }
1258141cc406Sopenharmony_ci    else
1259141cc406Sopenharmony_ci    {
1260141cc406Sopenharmony_ci        status = sense_handler (pss->fd, data, (void *) pss);
1261141cc406Sopenharmony_ci    }
1262141cc406Sopenharmony_ci    return status;
1263141cc406Sopenharmony_ci}
1264141cc406Sopenharmony_ci*/
1265141cc406Sopenharmony_ci
1266141cc406Sopenharmony_cistatic SANE_Status send_diagnostic (SnapScan_Scanner *pss)
1267141cc406Sopenharmony_ci{
1268141cc406Sopenharmony_ci    static const char *me = "send_diagnostic";
1269141cc406Sopenharmony_ci    u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0};    /* self-test */
1270141cc406Sopenharmony_ci    SANE_Status status;
1271141cc406Sopenharmony_ci
1272141cc406Sopenharmony_ci    if (pss->pdev->model == PRISA620
1273141cc406Sopenharmony_ci        ||
1274141cc406Sopenharmony_ci     pss->pdev->model == PRISA610
1275141cc406Sopenharmony_ci        ||
1276141cc406Sopenharmony_ci     pss->pdev->model == SNAPSCAN1236
1277141cc406Sopenharmony_ci        ||
1278141cc406Sopenharmony_ci     pss->pdev->model == SCANWIT2720S
1279141cc406Sopenharmony_ci        ||
1280141cc406Sopenharmony_ci        pss->pdev->model == ARCUS1200)
1281141cc406Sopenharmony_ci    {
1282141cc406Sopenharmony_ci        return SANE_STATUS_GOOD;
1283141cc406Sopenharmony_ci    }
1284141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1285141cc406Sopenharmony_ci
1286141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, cmd, sizeof (cmd), NULL, NULL);
1287141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1288141cc406Sopenharmony_ci    return status;
1289141cc406Sopenharmony_ci}
1290141cc406Sopenharmony_ci
1291141cc406Sopenharmony_cistatic SANE_Status wait_scanner_ready (SnapScan_Scanner *pss)
1292141cc406Sopenharmony_ci{
1293141cc406Sopenharmony_ci    static char me[] = "wait_scanner_ready";
1294141cc406Sopenharmony_ci    SANE_Status status;
1295141cc406Sopenharmony_ci    int retries;
1296141cc406Sopenharmony_ci
1297141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1298141cc406Sopenharmony_ci
1299141cc406Sopenharmony_ci    /* if the tray is returning to the start position
1300141cc406Sopenharmony_ci       no time to wait is returned by the scanner. We'll
1301141cc406Sopenharmony_ci       try several times and sleep 1 second between each try. */
1302141cc406Sopenharmony_ci    for (retries = 20; retries; retries--)
1303141cc406Sopenharmony_ci    {
1304141cc406Sopenharmony_ci        status = test_unit_ready (pss);
1305141cc406Sopenharmony_ci        switch (status)
1306141cc406Sopenharmony_ci        {
1307141cc406Sopenharmony_ci        case SANE_STATUS_GOOD:
1308141cc406Sopenharmony_ci            return status;
1309141cc406Sopenharmony_ci        case SANE_STATUS_DEVICE_BUSY:
1310141cc406Sopenharmony_ci            /* first additional sense byte contains time to wait */
1311141cc406Sopenharmony_ci            {
1312141cc406Sopenharmony_ci                int delay = pss->asi1;
1313141cc406Sopenharmony_ci                if (delay > 0)
1314141cc406Sopenharmony_ci                {
1315141cc406Sopenharmony_ci                    DBG (0,
1316141cc406Sopenharmony_ci                        "Scanner warming up - waiting %d seconds.\n",
1317141cc406Sopenharmony_ci                        delay);
1318141cc406Sopenharmony_ci                    sleep (delay);
1319141cc406Sopenharmony_ci                }
1320141cc406Sopenharmony_ci                else
1321141cc406Sopenharmony_ci                {
1322141cc406Sopenharmony_ci                    /* This seems to happen for Epson scanners. Return
1323141cc406Sopenharmony_ci                       SANE_STATUS_GOOD and hope the scanner accepts the
1324141cc406Sopenharmony_ci                       next command... */
1325141cc406Sopenharmony_ci                    DBG (DL_CALL_TRACE, "%s: No timeout specified, returning immediately\n", me);
1326141cc406Sopenharmony_ci                    return SANE_STATUS_GOOD;
1327141cc406Sopenharmony_ci                }
1328141cc406Sopenharmony_ci            }
1329141cc406Sopenharmony_ci            break;
1330141cc406Sopenharmony_ci        case SANE_STATUS_IO_ERROR:
1331141cc406Sopenharmony_ci            /* hardware error; bail */
1332141cc406Sopenharmony_ci            DBG (DL_MAJOR_ERROR, "%s: hardware error detected.\n", me);
1333141cc406Sopenharmony_ci            return status;
1334141cc406Sopenharmony_ci        case SANE_STATUS_JAMMED:
1335141cc406Sopenharmony_ci        case SANE_STATUS_NO_DOCS:
1336141cc406Sopenharmony_ci            return status;
1337141cc406Sopenharmony_ci        default:
1338141cc406Sopenharmony_ci            DBG (DL_MAJOR_ERROR,
1339141cc406Sopenharmony_ci                "%s: unhandled request_sense result; trying again.\n",
1340141cc406Sopenharmony_ci                me);
1341141cc406Sopenharmony_ci            break;
1342141cc406Sopenharmony_ci        }
1343141cc406Sopenharmony_ci    }
1344141cc406Sopenharmony_ci
1345141cc406Sopenharmony_ci    return status;
1346141cc406Sopenharmony_ci}
1347141cc406Sopenharmony_ci
1348141cc406Sopenharmony_ci#define READ_CALIBRATION 0x82
1349141cc406Sopenharmony_ci#define READ_CALIBRATION_BLACK 0x89
1350141cc406Sopenharmony_ci#define NUM_CALIBRATION_LINES 16
1351141cc406Sopenharmony_ci#define NUM_CALIBRATION_LINES_EPSON 48
1352141cc406Sopenharmony_ci#define NUM_CALIBRATION_LINES_EPSON_BLACK 128
1353141cc406Sopenharmony_ci
1354141cc406Sopenharmony_cistatic SANE_Status calibrate_epson (SnapScan_Scanner *pss)
1355141cc406Sopenharmony_ci{
1356141cc406Sopenharmony_ci    u_char *buf, *pbuf;
1357141cc406Sopenharmony_ci    int *bins;
1358141cc406Sopenharmony_ci    static const char *me = "calibrate_epson";
1359141cc406Sopenharmony_ci    /* pixels_per_line = 8.5 inch * resolution of one sensor */
1360141cc406Sopenharmony_ci    int pixels_per_line = pss->actual_res * 17/4;
1361141cc406Sopenharmony_ci    int num_bins = pixels_per_line;
1362141cc406Sopenharmony_ci    int i, j, k, loop_inc, tl;
1363141cc406Sopenharmony_ci    int r, g, b;
1364141cc406Sopenharmony_ci    size_t expected_read_bytes;
1365141cc406Sopenharmony_ci    size_t read_bytes;
1366141cc406Sopenharmony_ci    SANE_Status status;
1367141cc406Sopenharmony_ci    int pass;
1368141cc406Sopenharmony_ci    int cal_lines = NUM_CALIBRATION_LINES_EPSON;
1369141cc406Sopenharmony_ci    int dtc = READ_CALIBRATION;
1370141cc406Sopenharmony_ci    int bytes_per_bin = 1;
1371141cc406Sopenharmony_ci
1372141cc406Sopenharmony_ci    /* in 16 bit mode we get two bytes of cal data per bin */
1373141cc406Sopenharmony_ci    if (pss->bpp_scan == 16)
1374141cc406Sopenharmony_ci        bytes_per_bin = 2;
1375141cc406Sopenharmony_ci
1376141cc406Sopenharmony_ci    /* calculate number of bins depending on mode and resolution
1377141cc406Sopenharmony_ci     * colour mode requires bins for each of rgb
1378141cc406Sopenharmony_ci     * full resolution doubles it because of second sensor line */
1379141cc406Sopenharmony_ci    if (is_colour_mode(actual_mode(pss))) {
1380141cc406Sopenharmony_ci        num_bins *= 3;
1381141cc406Sopenharmony_ci    }
1382141cc406Sopenharmony_ci    if (pss->res >= (SANE_Int)pss->actual_res) {
1383141cc406Sopenharmony_ci        num_bins *= 2;
1384141cc406Sopenharmony_ci    }
1385141cc406Sopenharmony_ci
1386141cc406Sopenharmony_ci    /* allocate memory for bins, all the red, then green, then blue */
1387141cc406Sopenharmony_ci    bins = (int *) malloc (num_bins * sizeof (int));
1388141cc406Sopenharmony_ci    if (!bins) {
1389141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR, "%s: out of memory allocating bins, %ld bytes.", me, (u_long)num_bins * sizeof (int));
1390141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
1391141cc406Sopenharmony_ci    }
1392141cc406Sopenharmony_ci
1393141cc406Sopenharmony_ci    /* allocate buffer for receive data */
1394141cc406Sopenharmony_ci    expected_read_bytes = pixels_per_line * 3 * 4;
1395141cc406Sopenharmony_ci    buf = (u_char *) malloc (expected_read_bytes);
1396141cc406Sopenharmony_ci    if (!buf) {
1397141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %ld bytes.", me, (u_long)expected_read_bytes);
1398141cc406Sopenharmony_ci        free (bins);
1399141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
1400141cc406Sopenharmony_ci    }
1401141cc406Sopenharmony_ci
1402141cc406Sopenharmony_ci    loop_inc = expected_read_bytes / (num_bins * bytes_per_bin);
1403141cc406Sopenharmony_ci
1404141cc406Sopenharmony_ci    /* do two passes, first pass does basic calibration, second does transparency adaptor if in use */
1405141cc406Sopenharmony_ci    for (pass = 0; pass < 2; pass++) {
1406141cc406Sopenharmony_ci        if (pass == 1) {
1407141cc406Sopenharmony_ci            if (pss->source == SRC_TPO) {
1408141cc406Sopenharmony_ci                /* pass 1 is for black level calibration of transparency adaptor */
1409141cc406Sopenharmony_ci                cal_lines = NUM_CALIBRATION_LINES_EPSON_BLACK;
1410141cc406Sopenharmony_ci                dtc = READ_CALIBRATION_BLACK;
1411141cc406Sopenharmony_ci            } else
1412141cc406Sopenharmony_ci                continue;
1413141cc406Sopenharmony_ci        }
1414141cc406Sopenharmony_ci
1415141cc406Sopenharmony_ci        /* empty the bins */
1416141cc406Sopenharmony_ci        for (i = 0; i < num_bins; i++)
1417141cc406Sopenharmony_ci            bins[i] = 0;
1418141cc406Sopenharmony_ci
1419141cc406Sopenharmony_ci        for (i = 0; i < cal_lines; i += loop_inc) {
1420141cc406Sopenharmony_ci            /* get the calibration data */
1421141cc406Sopenharmony_ci            zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
1422141cc406Sopenharmony_ci            pss->cmd[0] = READ;
1423141cc406Sopenharmony_ci            pss->cmd[2] = dtc;
1424141cc406Sopenharmony_ci            pss->cmd[5] = cal_lines;
1425141cc406Sopenharmony_ci            if (cal_lines - i > loop_inc)
1426141cc406Sopenharmony_ci                expected_read_bytes = loop_inc * (num_bins * bytes_per_bin);
1427141cc406Sopenharmony_ci            else
1428141cc406Sopenharmony_ci                expected_read_bytes = (cal_lines - i) * (num_bins * bytes_per_bin);
1429141cc406Sopenharmony_ci
1430141cc406Sopenharmony_ci            u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
1431141cc406Sopenharmony_ci            read_bytes = expected_read_bytes;
1432141cc406Sopenharmony_ci
1433141cc406Sopenharmony_ci            status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
1434141cc406Sopenharmony_ci                         READ_LEN, buf, &read_bytes);
1435141cc406Sopenharmony_ci
1436141cc406Sopenharmony_ci            if (status != SANE_STATUS_GOOD) {
1437141cc406Sopenharmony_ci                DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "read_cal_2480", sane_strstatus(status));
1438141cc406Sopenharmony_ci                free (bins);
1439141cc406Sopenharmony_ci                free (buf);
1440141cc406Sopenharmony_ci                return status;
1441141cc406Sopenharmony_ci            }
1442141cc406Sopenharmony_ci
1443141cc406Sopenharmony_ci            if (read_bytes != expected_read_bytes) {
1444141cc406Sopenharmony_ci                DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
1445141cc406Sopenharmony_ci                free (bins);
1446141cc406Sopenharmony_ci                free (buf);
1447141cc406Sopenharmony_ci                return SANE_STATUS_IO_ERROR;
1448141cc406Sopenharmony_ci            }
1449141cc406Sopenharmony_ci
1450141cc406Sopenharmony_ci            /* add calibration results into bins */
1451141cc406Sopenharmony_ci            pbuf = buf;
1452141cc406Sopenharmony_ci            for (j = 0; j < (int)expected_read_bytes / (num_bins * bytes_per_bin); j++) {
1453141cc406Sopenharmony_ci              for (k = 0; k < num_bins; k++) {
1454141cc406Sopenharmony_ci                bins[k] += *pbuf++;
1455141cc406Sopenharmony_ci                /* add in second byte for 16bit mode */
1456141cc406Sopenharmony_ci                if (bytes_per_bin == 2) {
1457141cc406Sopenharmony_ci                  bins[k] += *pbuf++ * 256;
1458141cc406Sopenharmony_ci		}
1459141cc406Sopenharmony_ci              }
1460141cc406Sopenharmony_ci            }
1461141cc406Sopenharmony_ci        }
1462141cc406Sopenharmony_ci
1463141cc406Sopenharmony_ci        /* now make averages */
1464141cc406Sopenharmony_ci        for (k = 0; k < num_bins; k++) {
1465141cc406Sopenharmony_ci            bins[k] /= cal_lines;
1466141cc406Sopenharmony_ci            /* also divide by 64 for 16bit mode */
1467141cc406Sopenharmony_ci            if (bytes_per_bin == 2)
1468141cc406Sopenharmony_ci              bins[k] /= 64;
1469141cc406Sopenharmony_ci        }
1470141cc406Sopenharmony_ci        /* now fill up result buffer */
1471141cc406Sopenharmony_ci        r = g = b = 0;
1472141cc406Sopenharmony_ci
1473141cc406Sopenharmony_ci        /* create data to send back: start with r g b values, and follow with differences
1474141cc406Sopenharmony_ci         * to previous value */
1475141cc406Sopenharmony_ci        pbuf = pss->buf + SEND_LENGTH;
1476141cc406Sopenharmony_ci        if (is_colour_mode(actual_mode(pss))) {
1477141cc406Sopenharmony_ci            for (k = 0; k < num_bins / 3; k++) {
1478141cc406Sopenharmony_ci                *pbuf++ = bins[k] - r;
1479141cc406Sopenharmony_ci                r = bins[k];
1480141cc406Sopenharmony_ci                *pbuf++ = bins[k + num_bins/3] - g;
1481141cc406Sopenharmony_ci                g = bins[k + num_bins/3];
1482141cc406Sopenharmony_ci                *pbuf++ = bins[k + 2*num_bins/3] - b;
1483141cc406Sopenharmony_ci                b = bins[k + 2*num_bins/3];
1484141cc406Sopenharmony_ci            }
1485141cc406Sopenharmony_ci        } else {
1486141cc406Sopenharmony_ci            for (k = 0; k < num_bins; k++) {
1487141cc406Sopenharmony_ci                *pbuf++ = bins[k] - g;
1488141cc406Sopenharmony_ci                g = bins[k];
1489141cc406Sopenharmony_ci            }
1490141cc406Sopenharmony_ci        }
1491141cc406Sopenharmony_ci
1492141cc406Sopenharmony_ci        /* send back the calibration data; round up transfer length (to match windows driver) */
1493141cc406Sopenharmony_ci        zero_buf (pss->buf, SEND_LENGTH);
1494141cc406Sopenharmony_ci        pss->buf[0] = SEND;
1495141cc406Sopenharmony_ci        pss->buf[2] = dtc;
1496141cc406Sopenharmony_ci        tl = (num_bins + 0xff) & ~0xff;
1497141cc406Sopenharmony_ci        u_int_to_u_char3p (tl, pss->buf + 6);
1498141cc406Sopenharmony_ci        status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->buf, SEND_LENGTH + tl,
1499141cc406Sopenharmony_ci                                 NULL, NULL);
1500141cc406Sopenharmony_ci        if (status != SANE_STATUS_GOOD) {
1501141cc406Sopenharmony_ci            DBG(DL_MAJOR_ERROR, "%s: %s command failed: %s\n", me, "send_cal_2480", sane_strstatus(status));
1502141cc406Sopenharmony_ci            free (bins);
1503141cc406Sopenharmony_ci            free (buf);
1504141cc406Sopenharmony_ci            return status;
1505141cc406Sopenharmony_ci        }
1506141cc406Sopenharmony_ci    }
1507141cc406Sopenharmony_ci
1508141cc406Sopenharmony_ci    free (bins);
1509141cc406Sopenharmony_ci    free (buf);
1510141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1511141cc406Sopenharmony_ci}
1512141cc406Sopenharmony_ci
1513141cc406Sopenharmony_cistatic SANE_Status read_calibration_data (SnapScan_Scanner *pss, void *buf, u_char num_lines)
1514141cc406Sopenharmony_ci{
1515141cc406Sopenharmony_ci    static const char *me = "read_calibration_data";
1516141cc406Sopenharmony_ci    SANE_Status status;
1517141cc406Sopenharmony_ci    size_t expected_read_bytes = num_lines * calibration_line_length(pss);
1518141cc406Sopenharmony_ci    size_t read_bytes;
1519141cc406Sopenharmony_ci
1520141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1521141cc406Sopenharmony_ci    zero_buf (pss->cmd, MAX_SCSI_CMD_LEN);
1522141cc406Sopenharmony_ci    pss->cmd[0] = READ;
1523141cc406Sopenharmony_ci    pss->cmd[2] = READ_CALIBRATION;
1524141cc406Sopenharmony_ci    pss->cmd[5] = num_lines;
1525141cc406Sopenharmony_ci    u_int_to_u_char3p (expected_read_bytes, pss->cmd + 6);
1526141cc406Sopenharmony_ci    read_bytes = expected_read_bytes;
1527141cc406Sopenharmony_ci
1528141cc406Sopenharmony_ci    status = snapscan_cmd (pss->pdev->bus, pss->fd, pss->cmd,
1529141cc406Sopenharmony_ci                 READ_LEN, buf, &read_bytes);
1530141cc406Sopenharmony_ci    CHECK_STATUS (status, me, "snapscan_cmd");
1531141cc406Sopenharmony_ci
1532141cc406Sopenharmony_ci    if(read_bytes != expected_read_bytes) {
1533141cc406Sopenharmony_ci    DBG (DL_MAJOR_ERROR, "%s: read %lu of %lu calibration data\n", me, (u_long) read_bytes, (u_long) expected_read_bytes);
1534141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
1535141cc406Sopenharmony_ci    }
1536141cc406Sopenharmony_ci
1537141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1538141cc406Sopenharmony_ci}
1539141cc406Sopenharmony_ci
1540141cc406Sopenharmony_cistatic SANE_Status calibrate (SnapScan_Scanner *pss)
1541141cc406Sopenharmony_ci{
1542141cc406Sopenharmony_ci    int r;
1543141cc406Sopenharmony_ci    int c;
1544141cc406Sopenharmony_ci    u_char *buf;
1545141cc406Sopenharmony_ci    static const char *me = "calibrate";
1546141cc406Sopenharmony_ci    SANE_Status status;
1547141cc406Sopenharmony_ci    int line_length = calibration_line_length(pss);
1548141cc406Sopenharmony_ci
1549141cc406Sopenharmony_ci    if ((pss->pdev->model == PERFECTION2480) ||
1550141cc406Sopenharmony_ci        (pss->pdev->model == PERFECTION3490)) {
1551141cc406Sopenharmony_ci        return calibrate_epson (pss);
1552141cc406Sopenharmony_ci    }
1553141cc406Sopenharmony_ci
1554141cc406Sopenharmony_ci    if (pss->pdev->model == PRISA5150)
1555141cc406Sopenharmony_ci    {
1556141cc406Sopenharmony_ci        return send_calibration_5150(pss);
1557141cc406Sopenharmony_ci    }
1558141cc406Sopenharmony_ci
1559141cc406Sopenharmony_ci    DBG (DL_CALL_TRACE, "%s\n", me);
1560141cc406Sopenharmony_ci
1561141cc406Sopenharmony_ci    if (line_length) {
1562141cc406Sopenharmony_ci        int num_lines = pss->phys_buf_sz / line_length;
1563141cc406Sopenharmony_ci        if (num_lines > NUM_CALIBRATION_LINES)
1564141cc406Sopenharmony_ci        {
1565141cc406Sopenharmony_ci            num_lines = NUM_CALIBRATION_LINES;
1566141cc406Sopenharmony_ci        } else if (num_lines == 0)
1567141cc406Sopenharmony_ci        {
1568141cc406Sopenharmony_ci            DBG(DL_MAJOR_ERROR, "%s: scsi request size underflow (< %d bytes)", me, line_length);
1569141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
1570141cc406Sopenharmony_ci        }
1571141cc406Sopenharmony_ci        buf = (u_char *) malloc(num_lines * line_length);
1572141cc406Sopenharmony_ci        if (!buf)
1573141cc406Sopenharmony_ci        {
1574141cc406Sopenharmony_ci            DBG (DL_MAJOR_ERROR, "%s: out of memory allocating calibration, %d bytes.", me, num_lines * line_length);
1575141cc406Sopenharmony_ci            return SANE_STATUS_NO_MEM;
1576141cc406Sopenharmony_ci        }
1577141cc406Sopenharmony_ci
1578141cc406Sopenharmony_ci        DBG (DL_MAJOR_ERROR, "%s: reading calibration data (%d lines)\n", me, num_lines);
1579141cc406Sopenharmony_ci        status = read_calibration_data(pss, buf, num_lines);
1580141cc406Sopenharmony_ci        CHECK_STATUS(status, me, "read_calibration_data");
1581141cc406Sopenharmony_ci
1582141cc406Sopenharmony_ci        for(c=0; c < line_length; c++) {
1583141cc406Sopenharmony_ci            u_int sum = 0;
1584141cc406Sopenharmony_ci            for(r=0; r < num_lines; r++) {
1585141cc406Sopenharmony_ci                sum += buf[c + r * line_length];
1586141cc406Sopenharmony_ci            }
1587141cc406Sopenharmony_ci            pss->buf[c + SEND_LENGTH] = sum / num_lines;
1588141cc406Sopenharmony_ci        }
1589141cc406Sopenharmony_ci
1590141cc406Sopenharmony_ci        status = send (pss, DTC_CALIBRATION, 1);
1591141cc406Sopenharmony_ci        CHECK_STATUS(status, me, "send calibration");
1592141cc406Sopenharmony_ci        free(buf);
1593141cc406Sopenharmony_ci    }
1594141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1595141cc406Sopenharmony_ci}
1596141cc406Sopenharmony_ci
1597141cc406Sopenharmony_cistatic SANE_Status download_firmware(SnapScan_Scanner * pss)
1598141cc406Sopenharmony_ci{
1599141cc406Sopenharmony_ci    static const char *me = "download_firmware";
1600141cc406Sopenharmony_ci    unsigned char *pFwBuf, *pCDB;
1601141cc406Sopenharmony_ci    char* firmware = NULL;
1602141cc406Sopenharmony_ci    FILE *fd;
1603141cc406Sopenharmony_ci    size_t bufLength,cdbLength;
1604141cc406Sopenharmony_ci    SANE_Status status = SANE_STATUS_GOOD;
1605141cc406Sopenharmony_ci    char cModelName[8], cModel[255];
1606141cc406Sopenharmony_ci    unsigned char bModelNo;
1607141cc406Sopenharmony_ci
1608141cc406Sopenharmony_ci    bModelNo =*(pss->buf + INQUIRY_HWMI);
1609141cc406Sopenharmony_ci    zero_buf((unsigned char *)cModel, 255);
1610141cc406Sopenharmony_ci    sprintf(cModelName, "%d", bModelNo);
1611141cc406Sopenharmony_ci    DBG(DL_INFO, "Looking up %s\n", cModelName);
1612141cc406Sopenharmony_ci    if (pss->pdev->firmware_filename) {
1613141cc406Sopenharmony_ci        firmware = pss->pdev->firmware_filename;
1614141cc406Sopenharmony_ci    } else if (default_firmware_filename) {
1615141cc406Sopenharmony_ci        firmware = default_firmware_filename;
1616141cc406Sopenharmony_ci    } else {
1617141cc406Sopenharmony_ci        DBG (0,
1618141cc406Sopenharmony_ci            "%s: No firmware entry found in config file %s.\n",
1619141cc406Sopenharmony_ci            me,
1620141cc406Sopenharmony_ci            SNAPSCAN_CONFIG_FILE
1621141cc406Sopenharmony_ci        );
1622141cc406Sopenharmony_ci        status = SANE_STATUS_INVAL;
1623141cc406Sopenharmony_ci    }
1624141cc406Sopenharmony_ci    if (status == SANE_STATUS_GOOD)
1625141cc406Sopenharmony_ci    {
1626141cc406Sopenharmony_ci        cdbLength = 10;
1627141cc406Sopenharmony_ci        DBG(DL_INFO, "Downloading %s\n", firmware);
1628141cc406Sopenharmony_ci        fd = fopen(firmware,"rb");
1629141cc406Sopenharmony_ci        if(fd == NULL)
1630141cc406Sopenharmony_ci        {
1631141cc406Sopenharmony_ci            DBG (0, "Cannot open firmware file %s.\n", firmware);
1632141cc406Sopenharmony_ci            DBG (0, "Edit the firmware file entry in %s.\n", SNAPSCAN_CONFIG_FILE);
1633141cc406Sopenharmony_ci            status = SANE_STATUS_INVAL;
1634141cc406Sopenharmony_ci        }
1635141cc406Sopenharmony_ci        else
1636141cc406Sopenharmony_ci        {
1637141cc406Sopenharmony_ci            switch (pss->pdev->model)
1638141cc406Sopenharmony_ci            {
1639141cc406Sopenharmony_ci            case PRISA610:
1640141cc406Sopenharmony_ci            case PRISA310:
1641141cc406Sopenharmony_ci            case PRISA620:
1642141cc406Sopenharmony_ci            case PRISA1240:
1643141cc406Sopenharmony_ci            case PRISA640:
1644141cc406Sopenharmony_ci            case PRISA4300:
1645141cc406Sopenharmony_ci            case PRISA4300_2:
1646141cc406Sopenharmony_ci            case PRISA5000:
1647141cc406Sopenharmony_ci            case PRISA5000E:
1648141cc406Sopenharmony_ci            case PRISA5150:
1649141cc406Sopenharmony_ci            case PRISA5300:
1650141cc406Sopenharmony_ci            case STYLUS_CX1500:
1651141cc406Sopenharmony_ci                /* ACER firmware files do not contain an info block */
1652141cc406Sopenharmony_ci                fseek(fd, 0, SEEK_END);
1653141cc406Sopenharmony_ci                bufLength = ftell(fd);
1654141cc406Sopenharmony_ci                fseek(fd, 0, SEEK_SET);
1655141cc406Sopenharmony_ci                break;
1656141cc406Sopenharmony_ci            case PERFECTION1270:
1657141cc406Sopenharmony_ci            case PERFECTION1670:
1658141cc406Sopenharmony_ci            case PERFECTION2480:
1659141cc406Sopenharmony_ci            case PERFECTION3490:
1660141cc406Sopenharmony_ci                /* Epson firmware files contain an info block which
1661141cc406Sopenharmony_ci                   specifies the length of the firmware data. The
1662141cc406Sopenharmony_ci                   length information is stored at offset 0x64 from
1663141cc406Sopenharmony_ci                   end of file */
1664141cc406Sopenharmony_ci                {
1665141cc406Sopenharmony_ci                    unsigned char size_l, size_h;
1666141cc406Sopenharmony_ci                    fseek(fd, -0x64, SEEK_END);
1667141cc406Sopenharmony_ci                    fread(&size_l, 1, 1, fd);
1668141cc406Sopenharmony_ci                    fread(&size_h, 1, 1, fd);
1669141cc406Sopenharmony_ci                    fseek(fd, 0, SEEK_SET);
1670141cc406Sopenharmony_ci                    bufLength = (size_h << 8) + size_l;
1671141cc406Sopenharmony_ci                }
1672141cc406Sopenharmony_ci                break;
1673141cc406Sopenharmony_ci            default:
1674141cc406Sopenharmony_ci                /* AGFA firmware files contain an info block which
1675141cc406Sopenharmony_ci                   specifies the length of the firmware data. The
1676141cc406Sopenharmony_ci                   length information is stored at offset 0x5e from
1677141cc406Sopenharmony_ci                   end of file */
1678141cc406Sopenharmony_ci                {
1679141cc406Sopenharmony_ci                    unsigned char size_l, size_h;
1680141cc406Sopenharmony_ci                    fseek(fd, -0x5e, SEEK_END);
1681141cc406Sopenharmony_ci                    fread(&size_l, 1, 1, fd);
1682141cc406Sopenharmony_ci                    fread(&size_h, 1, 1, fd);
1683141cc406Sopenharmony_ci                    fseek(fd, 0, SEEK_SET);
1684141cc406Sopenharmony_ci                    bufLength = (size_h << 8) + size_l;
1685141cc406Sopenharmony_ci                }
1686141cc406Sopenharmony_ci                break;
1687141cc406Sopenharmony_ci            }
1688141cc406Sopenharmony_ci
1689141cc406Sopenharmony_ci            DBG(DL_INFO, "Size of firmware: %lu\n", (u_long) bufLength);
1690141cc406Sopenharmony_ci            pCDB = (unsigned char *)malloc(bufLength + cdbLength);
1691141cc406Sopenharmony_ci            pFwBuf = pCDB + cdbLength;
1692141cc406Sopenharmony_ci            zero_buf (pCDB, cdbLength);
1693141cc406Sopenharmony_ci            (void)fread(pFwBuf,1,bufLength,fd);
1694141cc406Sopenharmony_ci
1695141cc406Sopenharmony_ci            *pCDB = SEND;
1696141cc406Sopenharmony_ci            *(pCDB + 2) = 0x87;
1697141cc406Sopenharmony_ci            *(pCDB + 6) = (bufLength >> 16) & 0xff;
1698141cc406Sopenharmony_ci            *(pCDB + 7) = (bufLength >> 8) & 0xff;
1699141cc406Sopenharmony_ci            *(pCDB + 8) = bufLength  & 0xff;
1700141cc406Sopenharmony_ci
1701141cc406Sopenharmony_ci            status = snapscan_cmd (
1702141cc406Sopenharmony_ci                pss->pdev->bus, pss->fd, pCDB, bufLength+cdbLength, NULL, NULL);
1703141cc406Sopenharmony_ci            pss->firmware_loaded = SANE_TRUE;
1704141cc406Sopenharmony_ci
1705141cc406Sopenharmony_ci            free(pCDB);
1706141cc406Sopenharmony_ci            fclose(fd);
1707141cc406Sopenharmony_ci        }
1708141cc406Sopenharmony_ci    }
1709141cc406Sopenharmony_ci    return status;
1710141cc406Sopenharmony_ci}
1711141cc406Sopenharmony_ci
1712141cc406Sopenharmony_ci/*
1713141cc406Sopenharmony_ci * Revision 1.58  2006/09/03 10:00:11  oliver-guest
1714141cc406Sopenharmony_ci * Bugfix for firmware download by Paul Smedley
1715141cc406Sopenharmony_ci *
1716141cc406Sopenharmony_ci * Revision 1.57  2006/03/20 18:20:10  oliver-guest
1717141cc406Sopenharmony_ci * Limit inquiry length to 120 bytes if firmware is not yet loaded
1718141cc406Sopenharmony_ci *
1719141cc406Sopenharmony_ci * Revision 1.56  2006/01/10 19:32:16  oliver-guest
1720141cc406Sopenharmony_ci * Added 12 bit gamma tables for Epson Stylus CX-1500
1721141cc406Sopenharmony_ci *
1722141cc406Sopenharmony_ci * Revision 1.55  2006/01/06 20:59:17  oliver-guest
1723141cc406Sopenharmony_ci * Some fixes for the Epson Stylus CX 1500
1724141cc406Sopenharmony_ci *
1725141cc406Sopenharmony_ci * Revision 1.54  2006/01/01 22:57:01  oliver-guest
1726141cc406Sopenharmony_ci * Added calibration data for Benq 5150 / 5250, preliminary support for Epson Stylus CX 1500
1727141cc406Sopenharmony_ci *
1728141cc406Sopenharmony_ci * Revision 1.53  2005/12/05 20:38:22  oliver-guest
1729141cc406Sopenharmony_ci * Small bugfix for Benq 5150
1730141cc406Sopenharmony_ci *
1731141cc406Sopenharmony_ci * Revision 1.52  2005/12/04 15:03:00  oliver-guest
1732141cc406Sopenharmony_ci * Some fixes for Benq 5150
1733141cc406Sopenharmony_ci *
1734141cc406Sopenharmony_ci * Revision 1.51  2005/11/26 18:53:03  oliver-guest
1735141cc406Sopenharmony_ci * Fix inquiry bug for Benq 5000
1736141cc406Sopenharmony_ci *
1737141cc406Sopenharmony_ci * Revision 1.50  2005/11/17 23:47:10  oliver-guest
1738141cc406Sopenharmony_ci * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
1739141cc406Sopenharmony_ci *
1740141cc406Sopenharmony_ci * Revision 1.49  2005/11/17 23:32:22  oliver-guest
1741141cc406Sopenharmony_ci * Fixes for Epson 3490 @ 2400 DPI
1742141cc406Sopenharmony_ci *
1743141cc406Sopenharmony_ci * Revision 1.48  2005/11/15 20:11:19  oliver-guest
1744141cc406Sopenharmony_ci * Enabled quality calibration for the Epson 3490
1745141cc406Sopenharmony_ci *
1746141cc406Sopenharmony_ci * Revision 1.47  2005/11/02 19:22:06  oliver-guest
1747141cc406Sopenharmony_ci * Fixes for Benq 5000
1748141cc406Sopenharmony_ci *
1749141cc406Sopenharmony_ci * Revision 1.46  2005/10/31 21:08:47  oliver-guest
1750141cc406Sopenharmony_ci * Distinguish between Benq 5000/5000E/5000U
1751141cc406Sopenharmony_ci *
1752141cc406Sopenharmony_ci * Revision 1.45  2005/10/24 19:46:40  oliver-guest
1753141cc406Sopenharmony_ci * Preview and range fix for Epson 2480/2580
1754141cc406Sopenharmony_ci *
1755141cc406Sopenharmony_ci * Revision 1.44  2005/10/23 21:28:58  oliver-guest
1756141cc406Sopenharmony_ci * Fix for buffer size in high res modes, fixes for delay code
1757141cc406Sopenharmony_ci *
1758141cc406Sopenharmony_ci * Revision 1.43  2005/10/20 21:23:53  oliver-guest
1759141cc406Sopenharmony_ci * Fixes for 16 bit quality calibration by Simon Munton
1760141cc406Sopenharmony_ci *
1761141cc406Sopenharmony_ci * Revision 1.42  2005/10/13 22:43:30  oliver-guest
1762141cc406Sopenharmony_ci * Fixes for 16 bit scan mode from Simon Munton
1763141cc406Sopenharmony_ci *
1764141cc406Sopenharmony_ci * Revision 1.41  2005/10/11 18:47:07  oliver-guest
1765141cc406Sopenharmony_ci * Fixes for Epson 3490 and 16 bit scan mode
1766141cc406Sopenharmony_ci *
1767141cc406Sopenharmony_ci * Revision 1.40  2005/09/28 22:09:26  oliver-guest
1768141cc406Sopenharmony_ci * Re-enabled enhanced inquiry command for Epson scanners (duh\!)
1769141cc406Sopenharmony_ci *
1770141cc406Sopenharmony_ci * Revision 1.39  2005/09/28 21:33:10  oliver-guest
1771141cc406Sopenharmony_ci * Added 16 bit option for Epson scanners (untested)
1772141cc406Sopenharmony_ci *
1773141cc406Sopenharmony_ci * Revision 1.38  2005/09/25 08:19:12  oliver-guest
1774141cc406Sopenharmony_ci * Removed debugging code for Epson scanners
1775141cc406Sopenharmony_ci *
1776141cc406Sopenharmony_ci * Revision 1.37  2005/09/03 11:31:31  oliver-guest
1777141cc406Sopenharmony_ci * Another small bugfix
1778141cc406Sopenharmony_ci *
1779141cc406Sopenharmony_ci * Revision 1.36  2005/09/03 10:52:11  oliver-guest
1780141cc406Sopenharmony_ci * Fixed debugging code for epson scanners
1781141cc406Sopenharmony_ci *
1782141cc406Sopenharmony_ci * Revision 1.35  2005/08/16 17:19:20  oliver-guest
1783141cc406Sopenharmony_ci * Make compileable again
1784141cc406Sopenharmony_ci *
1785141cc406Sopenharmony_ci * Revision 1.34  2005/08/15 18:56:55  oliver-guest
1786141cc406Sopenharmony_ci * Added temporary debug code for 2480/2580 distinction
1787141cc406Sopenharmony_ci *
1788141cc406Sopenharmony_ci * Revision 1.33  2005/08/15 18:06:37  oliver-guest
1789141cc406Sopenharmony_ci * Added support for Epson 3490/3590 (thanks to Matt Judge)
1790141cc406Sopenharmony_ci *
1791141cc406Sopenharmony_ci * Revision 1.32  2005/07/18 17:37:37  oliver-guest
1792141cc406Sopenharmony_ci * ZETA changes for SnapScan backend
1793141cc406Sopenharmony_ci *
1794141cc406Sopenharmony_ci * Revision 1.31  2004/12/09 23:21:48  oliver-guest
1795141cc406Sopenharmony_ci * Added quality calibration for Epson 2480 (by Simon Munton)
1796141cc406Sopenharmony_ci *
1797141cc406Sopenharmony_ci * Revision 1.30  2004/12/01 22:12:03  oliver-guest
1798141cc406Sopenharmony_ci * Added support for Epson 1270
1799141cc406Sopenharmony_ci *
1800141cc406Sopenharmony_ci * Revision 1.29  2004/10/03 17:34:36  hmg-guest
1801141cc406Sopenharmony_ci * 64 bit platform fixes (bug #300799).
1802141cc406Sopenharmony_ci *
1803141cc406Sopenharmony_ci * Revision 1.28  2004/09/02 20:59:11  oliver-guest
1804141cc406Sopenharmony_ci * Added support for Epson 2480
1805141cc406Sopenharmony_ci *
1806141cc406Sopenharmony_ci * Revision 1.27  2004/04/02 20:19:24  oliver-guest
1807141cc406Sopenharmony_ci * Various bugfixes for gamma correction (thanks to Robert Tsien)
1808141cc406Sopenharmony_ci *
1809141cc406Sopenharmony_ci * Revision 1.26  2003/11/07 23:26:49  oliver-guest
1810141cc406Sopenharmony_ci * Final bugfixes for bascic support of Epson 1670
1811141cc406Sopenharmony_ci *
1812141cc406Sopenharmony_ci * Revision 1.25  2003/10/21 20:43:25  oliver-guest
1813141cc406Sopenharmony_ci * Bugfixes for SnapScan backend
1814141cc406Sopenharmony_ci *
1815141cc406Sopenharmony_ci * Revision 1.24  2003/10/07 19:41:34  oliver-guest
1816141cc406Sopenharmony_ci * Updates for Epson Perfection 1670
1817141cc406Sopenharmony_ci *
1818141cc406Sopenharmony_ci * Revision 1.23  2003/08/19 21:05:08  oliverschwartz
1819141cc406Sopenharmony_ci * Scanner ID cleanup
1820141cc406Sopenharmony_ci *
1821141cc406Sopenharmony_ci * Revision 1.22  2003/04/30 20:49:39  oliverschwartz
1822141cc406Sopenharmony_ci * SnapScan backend 1.4.26
1823141cc406Sopenharmony_ci *
1824141cc406Sopenharmony_ci * Revision 1.37  2003/04/30 20:42:19  oliverschwartz
1825141cc406Sopenharmony_ci * Added support for Agfa Arcus 1200 (supplied by Valtteri Vuorikoski)
1826141cc406Sopenharmony_ci *
1827141cc406Sopenharmony_ci * Revision 1.36  2003/04/02 21:17:13  oliverschwartz
1828141cc406Sopenharmony_ci * Fix for 1200 DPI with Acer 5000
1829141cc406Sopenharmony_ci *
1830141cc406Sopenharmony_ci * Revision 1.35  2003/02/08 10:45:09  oliverschwartz
1831141cc406Sopenharmony_ci * Use 600 DPI as optical resolution for Benq 5000
1832141cc406Sopenharmony_ci *
1833141cc406Sopenharmony_ci * Revision 1.34  2002/12/10 20:14:12  oliverschwartz
1834141cc406Sopenharmony_ci * Enable color offset correction for SnapScan300
1835141cc406Sopenharmony_ci *
1836141cc406Sopenharmony_ci * Revision 1.33  2002/09/24 16:07:48  oliverschwartz
1837141cc406Sopenharmony_ci * Added support for Benq 5000
1838141cc406Sopenharmony_ci *
1839141cc406Sopenharmony_ci * Revision 1.32  2002/06/06 20:40:01  oliverschwartz
1840141cc406Sopenharmony_ci * Changed default scan area for transparency unit of SnapScan e50
1841141cc406Sopenharmony_ci *
1842141cc406Sopenharmony_ci * Revision 1.31  2002/05/02 18:28:44  oliverschwartz
1843141cc406Sopenharmony_ci * Added ADF support
1844141cc406Sopenharmony_ci *
1845141cc406Sopenharmony_ci * Revision 1.30  2002/04/27 14:41:22  oliverschwartz
1846141cc406Sopenharmony_ci * Print number of open handles in close_scanner()
1847141cc406Sopenharmony_ci *
1848141cc406Sopenharmony_ci * Revision 1.29  2002/04/10 21:46:48  oliverschwartz
1849141cc406Sopenharmony_ci * Removed illegal character
1850141cc406Sopenharmony_ci *
1851141cc406Sopenharmony_ci * Revision 1.28  2002/04/10 21:01:02  oliverschwartz
1852141cc406Sopenharmony_ci * Disable send_diagnostic() for 1236s
1853141cc406Sopenharmony_ci *
1854141cc406Sopenharmony_ci * Revision 1.27  2002/03/24 12:11:20  oliverschwartz
1855141cc406Sopenharmony_ci * Get name of firmware file in sane_init
1856141cc406Sopenharmony_ci *
1857141cc406Sopenharmony_ci * Revision 1.26  2002/01/23 20:42:41  oliverschwartz
1858141cc406Sopenharmony_ci * Improve recognition of Acer 320U
1859141cc406Sopenharmony_ci * Add sense_handler code for sense code 0x0b
1860141cc406Sopenharmony_ci * Fix for spaces in model strings
1861141cc406Sopenharmony_ci *
1862141cc406Sopenharmony_ci * Revision 1.25  2001/12/12 19:44:59  oliverschwartz
1863141cc406Sopenharmony_ci * Clean up CVS log
1864141cc406Sopenharmony_ci *
1865141cc406Sopenharmony_ci * Revision 1.24  2001/12/09 23:01:00  oliverschwartz
1866141cc406Sopenharmony_ci * - use sense handler for USB
1867141cc406Sopenharmony_ci * - fix scan mode
1868141cc406Sopenharmony_ci *
1869141cc406Sopenharmony_ci * Revision 1.23  2001/12/08 11:53:31  oliverschwartz
1870141cc406Sopenharmony_ci * - Additional logging in sense handler
1871141cc406Sopenharmony_ci * - Fix wait_scanner_ready() if device reports busy
1872141cc406Sopenharmony_ci * - Fix scanning mode (preview/normal)
1873141cc406Sopenharmony_ci *
1874141cc406Sopenharmony_ci * Revision 1.22  2001/11/27 23:16:17  oliverschwartz
1875141cc406Sopenharmony_ci * - Fix color alignment for SnapScan 600
1876141cc406Sopenharmony_ci * - Added documentation in snapscan-sources.c
1877141cc406Sopenharmony_ci * - Guard against TL_X < BR_X and TL_Y < BR_Y
1878141cc406Sopenharmony_ci *
1879141cc406Sopenharmony_ci * Revision 1.21  2001/10/21 08:49:37  oliverschwartz
1880141cc406Sopenharmony_ci * correct number of scan lines for calibration thanks to Mikko Ty���vi
1881141cc406Sopenharmony_ci *
1882141cc406Sopenharmony_ci * Revision 1.20  2001/10/12 20:54:04  oliverschwartz
1883141cc406Sopenharmony_ci * enable gamma correction for Snapscan 1236, e20 and e50 scanners
1884141cc406Sopenharmony_ci *
1885141cc406Sopenharmony_ci * Revision 1.19  2001/10/11 14:02:10  oliverschwartz
1886141cc406Sopenharmony_ci * Distinguish between e20/e25 and e40/e50
1887141cc406Sopenharmony_ci *
1888141cc406Sopenharmony_ci * Revision 1.18  2001/10/09 22:34:23  oliverschwartz
1889141cc406Sopenharmony_ci * fix compiler warnings
1890141cc406Sopenharmony_ci *
1891141cc406Sopenharmony_ci * Revision 1.17  2001/10/08 19:26:01  oliverschwartz
1892141cc406Sopenharmony_ci * - Disable quality calibration for scanners that do not support it
1893141cc406Sopenharmony_ci *
1894141cc406Sopenharmony_ci * Revision 1.16  2001/10/08 18:22:01  oliverschwartz
1895141cc406Sopenharmony_ci * - Disable quality calibration for Acer Vuego 310F
1896141cc406Sopenharmony_ci * - Use sanei_scsi_max_request_size as scanner buffer size
1897141cc406Sopenharmony_ci *   for SCSI devices
1898141cc406Sopenharmony_ci * - Added new devices to snapscan.desc
1899141cc406Sopenharmony_ci *
1900141cc406Sopenharmony_ci * Revision 1.15  2001/09/18 15:01:07  oliverschwartz
1901141cc406Sopenharmony_ci * - Read scanner id string again after firmware upload
1902141cc406Sopenharmony_ci *   to identify correct model
1903141cc406Sopenharmony_ci * - Make firmware upload work for AGFA scanners
1904141cc406Sopenharmony_ci * - Change copyright notice
1905141cc406Sopenharmony_ci *
1906141cc406Sopenharmony_ci * Revision 1.14  2001/09/17 10:01:08  sable
1907141cc406Sopenharmony_ci * Added model AGFA 1236U
1908141cc406Sopenharmony_ci *
1909141cc406Sopenharmony_ci * Revision 1.13  2001/09/09 18:06:32  oliverschwartz
1910141cc406Sopenharmony_ci * 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
1911141cc406Sopenharmony_ci *
1912141cc406Sopenharmony_ci * Revision 1.12  2001/04/10 13:00:31  sable
1913141cc406Sopenharmony_ci * Moving sanei_usb_* to snapscani_usb*
1914141cc406Sopenharmony_ci *
1915141cc406Sopenharmony_ci * Revision 1.11  2001/04/10 11:04:31  sable
1916141cc406Sopenharmony_ci * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
1917141cc406Sopenharmony_ci *
1918141cc406Sopenharmony_ci * Revision 1.10  2001/03/17 22:53:21  sable
1919141cc406Sopenharmony_ci * Applying Mikael Magnusson patch concerning Gamma correction
1920141cc406Sopenharmony_ci * Support for 1212U_2
1921141cc406Sopenharmony_ci *
1922141cc406Sopenharmony_ci * Revision 1.9  2000/11/10 01:01:59  sable
1923141cc406Sopenharmony_ci * USB (kind of) autodetection
1924141cc406Sopenharmony_ci *
1925141cc406Sopenharmony_ci * Revision 1.8  2000/11/01 01:26:43  sable
1926141cc406Sopenharmony_ci * Support for 1212U
1927141cc406Sopenharmony_ci *
1928141cc406Sopenharmony_ci * Revision 1.7  2000/10/30 22:32:20  sable
1929141cc406Sopenharmony_ci * Support for vuego310s vuego610s and 1236s
1930141cc406Sopenharmony_ci *
1931141cc406Sopenharmony_ci * Revision 1.6  2000/10/29 22:44:55  sable
1932141cc406Sopenharmony_ci * Bug correction for 1236s
1933141cc406Sopenharmony_ci *
1934141cc406Sopenharmony_ci * Revision 1.5  2000/10/28 14:16:10  sable
1935141cc406Sopenharmony_ci * Bug correction for SnapScan310
1936141cc406Sopenharmony_ci *
1937141cc406Sopenharmony_ci * Revision 1.4  2000/10/28 14:06:35  sable
1938141cc406Sopenharmony_ci * Add support for Acer300f
1939141cc406Sopenharmony_ci *
1940141cc406Sopenharmony_ci * Revision 1.3  2000/10/15 19:52:06  cbagwell
1941141cc406Sopenharmony_ci * Changed USB support to a 1 line modification instead of multi-file
1942141cc406Sopenharmony_ci * changes.
1943141cc406Sopenharmony_ci *
1944141cc406Sopenharmony_ci * Revision 1.2  2000/10/13 03:50:27  cbagwell
1945141cc406Sopenharmony_ci * Updating to source from SANE 1.0.3.  Calling this version 1.1
1946141cc406Sopenharmony_ci *
1947141cc406Sopenharmony_ci * */
1948