1141cc406Sopenharmony_ci/*
2141cc406Sopenharmony_ci * SANE backend for Xerox Phaser 3200MFP et al.
3141cc406Sopenharmony_ci * Copyright 2008-2016 ABC <abc@telekom.ru>
4141cc406Sopenharmony_ci *
5141cc406Sopenharmony_ci * Network Scanners Support
6141cc406Sopenharmony_ci * Copyright 2010 Alexander Kuznetsov <acca(at)cpan.org>
7141cc406Sopenharmony_ci *
8141cc406Sopenharmony_ci * Color scanning on Samsung M2870 model and Xerox Cognac 3215 & 3225
9141cc406Sopenharmony_ci * models by Laxmeesh Onkar Markod <m.laxmeesh@samsung.com>
10141cc406Sopenharmony_ci *
11141cc406Sopenharmony_ci * This program is licensed under GPL + SANE exception.
12141cc406Sopenharmony_ci * More info at http://www.sane-project.org/license.html
13141cc406Sopenharmony_ci */
14141cc406Sopenharmony_ci
15141cc406Sopenharmony_ci#define DEBUG_NOT_STATIC
16141cc406Sopenharmony_ci#define BACKEND_NAME xerox_mfp
17141cc406Sopenharmony_ci
18141cc406Sopenharmony_ci#include "../include/sane/config.h"
19141cc406Sopenharmony_ci#include "../include/lassert.h"
20141cc406Sopenharmony_ci#include <ctype.h>
21141cc406Sopenharmony_ci#include <stdlib.h>
22141cc406Sopenharmony_ci#include <string.h>
23141cc406Sopenharmony_ci#include <errno.h>
24141cc406Sopenharmony_ci#include <fcntl.h>
25141cc406Sopenharmony_ci#include <math.h>
26141cc406Sopenharmony_ci#include <unistd.h>
27141cc406Sopenharmony_ci#include <sys/time.h>
28141cc406Sopenharmony_ci#include <sys/types.h>
29141cc406Sopenharmony_ci#include "../include/sane/sane.h"
30141cc406Sopenharmony_ci#include "../include/sane/sanei.h"
31141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
32141cc406Sopenharmony_ci#include "../include/sane/sanei_thread.h"
33141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
34141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
35141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
36141cc406Sopenharmony_ci#ifdef HAVE_LIBJPEG
37141cc406Sopenharmony_ci#include <jpeglib.h>
38141cc406Sopenharmony_ci#endif
39141cc406Sopenharmony_ci#include "xerox_mfp.h"
40141cc406Sopenharmony_ci
41141cc406Sopenharmony_ci#define BACKEND_BUILD 13
42141cc406Sopenharmony_ci#define XEROX_CONFIG_FILE "xerox_mfp.conf"
43141cc406Sopenharmony_ci
44141cc406Sopenharmony_cistatic const SANE_Device **devlist = NULL;	/* sane_get_devices array */
45141cc406Sopenharmony_cistatic struct device *devices_head = NULL;	/* sane_get_devices list */
46141cc406Sopenharmony_ci
47141cc406Sopenharmony_cienum { TRANSPORT_USB, TRANSPORT_TCP, TRANSPORTS_MAX };
48141cc406Sopenharmony_citransport available_transports[TRANSPORTS_MAX] = {
49141cc406Sopenharmony_ci    { "usb", usb_dev_request, usb_dev_open, usb_dev_close, usb_configure_device },
50141cc406Sopenharmony_ci    { "tcp", tcp_dev_request, tcp_dev_open, tcp_dev_close, tcp_configure_device },
51141cc406Sopenharmony_ci};
52141cc406Sopenharmony_ci
53141cc406Sopenharmony_cistatic int resolv_state(int state)
54141cc406Sopenharmony_ci{
55141cc406Sopenharmony_ci    if (state & STATE_DOCUMENT_JAM)
56141cc406Sopenharmony_ci        return SANE_STATUS_JAMMED;
57141cc406Sopenharmony_ci    if (state & STATE_NO_DOCUMENT)
58141cc406Sopenharmony_ci        return SANE_STATUS_NO_DOCS;
59141cc406Sopenharmony_ci    if (state & STATE_COVER_OPEN)
60141cc406Sopenharmony_ci        return SANE_STATUS_COVER_OPEN;
61141cc406Sopenharmony_ci    if (state & STATE_INVALID_AREA)
62141cc406Sopenharmony_ci        return SANE_STATUS_INVAL; /* sane_start: implies SANE_INFO_RELOAD_OPTIONS */
63141cc406Sopenharmony_ci    if (state & STATE_WARMING)
64141cc406Sopenharmony_ci#ifdef SANE_STATUS_WARMING_UP
65141cc406Sopenharmony_ci        return SANE_STATUS_WARMING_UP;
66141cc406Sopenharmony_ci#else
67141cc406Sopenharmony_ci        return SANE_STATUS_DEVICE_BUSY;
68141cc406Sopenharmony_ci#endif
69141cc406Sopenharmony_ci    if (state & STATE_LOCKING)
70141cc406Sopenharmony_ci#ifdef SANE_STATUS_HW_LOCKED
71141cc406Sopenharmony_ci        return SANE_STATUS_HW_LOCKED;
72141cc406Sopenharmony_ci#else
73141cc406Sopenharmony_ci        return SANE_STATUS_JAMMED;
74141cc406Sopenharmony_ci#endif
75141cc406Sopenharmony_ci    if (state & ~STATE_NO_ERROR)
76141cc406Sopenharmony_ci        return SANE_STATUS_DEVICE_BUSY;
77141cc406Sopenharmony_ci    return 0;
78141cc406Sopenharmony_ci}
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_cistatic char *str_cmd(int cmd)
81141cc406Sopenharmony_ci{
82141cc406Sopenharmony_ci    switch (cmd) {
83141cc406Sopenharmony_ci    case CMD_ABORT:		return "ABORT";
84141cc406Sopenharmony_ci    case CMD_INQUIRY:		return "INQUIRY";
85141cc406Sopenharmony_ci    case CMD_RESERVE_UNIT:	return "RESERVE_UNIT";
86141cc406Sopenharmony_ci    case CMD_RELEASE_UNIT:	return "RELEASE_UNIT";
87141cc406Sopenharmony_ci    case CMD_SET_WINDOW:	return "SET_WINDOW";
88141cc406Sopenharmony_ci    case CMD_READ:		return "READ";
89141cc406Sopenharmony_ci    case CMD_READ_IMAGE:	return "READ_IMAGE";
90141cc406Sopenharmony_ci    case CMD_OBJECT_POSITION:	return "OBJECT_POSITION";
91141cc406Sopenharmony_ci    }
92141cc406Sopenharmony_ci    return "unknown";
93141cc406Sopenharmony_ci}
94141cc406Sopenharmony_ci
95141cc406Sopenharmony_ci#define MAX_DUMP 70
96141cc406Sopenharmony_ciconst char *encTmpFileName = "/tmp/stmp_enc.tmp";
97141cc406Sopenharmony_ci
98141cc406Sopenharmony_ci/*
99141cc406Sopenharmony_ci * Decode jpeg from `infilename` into dev->decData of dev->decDataSize size.
100141cc406Sopenharmony_ci */
101141cc406Sopenharmony_cistatic int decompress(struct device __sane_unused__ *dev,
102141cc406Sopenharmony_ci                      const char __sane_unused__ *infilename)
103141cc406Sopenharmony_ci{
104141cc406Sopenharmony_ci#ifdef HAVE_LIBJPEG
105141cc406Sopenharmony_ci    int rc;
106141cc406Sopenharmony_ci    int row_stride, width, height, pixel_size;
107141cc406Sopenharmony_ci    struct jpeg_decompress_struct cinfo;
108141cc406Sopenharmony_ci    struct jpeg_error_mgr jerr;
109141cc406Sopenharmony_ci    unsigned long bmp_size = 0;
110141cc406Sopenharmony_ci    FILE *pInfile = NULL;
111141cc406Sopenharmony_ci    JSAMPARRAY buffer;
112141cc406Sopenharmony_ci
113141cc406Sopenharmony_ci    if ((pInfile = fopen(infilename, "rb")) == NULL) {
114141cc406Sopenharmony_ci        fprintf(stderr, "can't open %s\n", infilename);
115141cc406Sopenharmony_ci        return -1;
116141cc406Sopenharmony_ci    }
117141cc406Sopenharmony_ci
118141cc406Sopenharmony_ci    cinfo.err = jpeg_std_error(&jerr);
119141cc406Sopenharmony_ci
120141cc406Sopenharmony_ci    jpeg_create_decompress(&cinfo);
121141cc406Sopenharmony_ci
122141cc406Sopenharmony_ci    jpeg_stdio_src(&cinfo, pInfile);
123141cc406Sopenharmony_ci
124141cc406Sopenharmony_ci    rc = jpeg_read_header(&cinfo, TRUE);
125141cc406Sopenharmony_ci    if (rc != 1) {
126141cc406Sopenharmony_ci        jpeg_destroy_decompress(&cinfo);
127141cc406Sopenharmony_ci        fclose(pInfile);
128141cc406Sopenharmony_ci        return -1;
129141cc406Sopenharmony_ci    }
130141cc406Sopenharmony_ci
131141cc406Sopenharmony_ci    jpeg_start_decompress(&cinfo);
132141cc406Sopenharmony_ci
133141cc406Sopenharmony_ci    width = cinfo.output_width;
134141cc406Sopenharmony_ci    height = cinfo.output_height;
135141cc406Sopenharmony_ci    pixel_size = cinfo.output_components;
136141cc406Sopenharmony_ci    bmp_size = width * height * pixel_size;
137141cc406Sopenharmony_ci    assert(bmp_size <= POST_DATASIZE);
138141cc406Sopenharmony_ci    dev->decDataSize = bmp_size;
139141cc406Sopenharmony_ci
140141cc406Sopenharmony_ci    row_stride = width * pixel_size;
141141cc406Sopenharmony_ci
142141cc406Sopenharmony_ci    buffer = (*cinfo.mem->alloc_sarray)
143141cc406Sopenharmony_ci             ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
144141cc406Sopenharmony_ci
145141cc406Sopenharmony_ci    while (cinfo.output_scanline < cinfo.output_height) {
146141cc406Sopenharmony_ci        buffer[0] = dev->decData + \
147141cc406Sopenharmony_ci                    (cinfo.output_scanline) * row_stride;
148141cc406Sopenharmony_ci        jpeg_read_scanlines(&cinfo, buffer, 1);
149141cc406Sopenharmony_ci    }
150141cc406Sopenharmony_ci    jpeg_finish_decompress(&cinfo);
151141cc406Sopenharmony_ci    jpeg_destroy_decompress(&cinfo);
152141cc406Sopenharmony_ci    fclose(pInfile);
153141cc406Sopenharmony_ci    return 0;
154141cc406Sopenharmony_ci#else
155141cc406Sopenharmony_ci    return -1;
156141cc406Sopenharmony_ci#endif
157141cc406Sopenharmony_ci}
158141cc406Sopenharmony_ci
159141cc406Sopenharmony_ci/* copy from decoded jpeg image (dev->decData) into user's buffer (pDest) */
160141cc406Sopenharmony_ci/* returns 0 if there is no data to copy */
161141cc406Sopenharmony_cistatic int copy_decompress_data(struct device *dev, unsigned char *pDest, int maxlen, int *destLen)
162141cc406Sopenharmony_ci{
163141cc406Sopenharmony_ci    int data_size = 0;
164141cc406Sopenharmony_ci
165141cc406Sopenharmony_ci    if (destLen)
166141cc406Sopenharmony_ci	*destLen = 0;
167141cc406Sopenharmony_ci    if (!dev->decDataSize)
168141cc406Sopenharmony_ci        return 0;
169141cc406Sopenharmony_ci    data_size = dev->decDataSize - dev->currentDecDataIndex;
170141cc406Sopenharmony_ci    if (data_size > maxlen)
171141cc406Sopenharmony_ci        data_size = maxlen;
172141cc406Sopenharmony_ci    if (data_size && pDest) {
173141cc406Sopenharmony_ci	memcpy(pDest, dev->decData + dev->currentDecDataIndex, data_size);
174141cc406Sopenharmony_ci	if (destLen)
175141cc406Sopenharmony_ci	    *destLen = data_size;
176141cc406Sopenharmony_ci	dev->currentDecDataIndex += data_size;
177141cc406Sopenharmony_ci    }
178141cc406Sopenharmony_ci    if (dev->decDataSize == dev->currentDecDataIndex) {
179141cc406Sopenharmony_ci        dev->currentDecDataIndex = 0;
180141cc406Sopenharmony_ci        dev->decDataSize = 0;
181141cc406Sopenharmony_ci    }
182141cc406Sopenharmony_ci    return 1;
183141cc406Sopenharmony_ci}
184141cc406Sopenharmony_ci
185141cc406Sopenharmony_cistatic int decompress_tempfile(struct device *dev)
186141cc406Sopenharmony_ci{
187141cc406Sopenharmony_ci    decompress(dev, encTmpFileName);
188141cc406Sopenharmony_ci    remove(encTmpFileName);
189141cc406Sopenharmony_ci    return 0;
190141cc406Sopenharmony_ci}
191141cc406Sopenharmony_ci
192141cc406Sopenharmony_cistatic int dump_to_tmp_file(struct device *dev)
193141cc406Sopenharmony_ci{
194141cc406Sopenharmony_ci    unsigned char *pSrc = dev->data;
195141cc406Sopenharmony_ci    int srcLen = dev->datalen;
196141cc406Sopenharmony_ci    FILE *pInfile;
197141cc406Sopenharmony_ci    if ((pInfile = fopen(encTmpFileName, "a")) == NULL) {
198141cc406Sopenharmony_ci        fprintf(stderr, "can't open %s\n", encTmpFileName);
199141cc406Sopenharmony_ci        return 0;
200141cc406Sopenharmony_ci    }
201141cc406Sopenharmony_ci
202141cc406Sopenharmony_ci    fwrite(pSrc, 1, srcLen, pInfile);
203141cc406Sopenharmony_ci    fclose(pInfile);
204141cc406Sopenharmony_ci    return srcLen;
205141cc406Sopenharmony_ci}
206141cc406Sopenharmony_ci
207141cc406Sopenharmony_cistatic int isSupportedDevice(struct device __sane_unused__ *dev)
208141cc406Sopenharmony_ci{
209141cc406Sopenharmony_ci#ifdef HAVE_LIBJPEG
210141cc406Sopenharmony_ci    /* Checking device which supports JPEG Lossy compression for color scanning*/
211141cc406Sopenharmony_ci    if (dev->compressionTypes & (1 << 6)) {
212141cc406Sopenharmony_ci	/* blacklist malfunctioning device(s) */
213141cc406Sopenharmony_ci	if (!strncmp(dev->sane.model, "SCX-4500W", 9) ||
214141cc406Sopenharmony_ci            !strncmp(dev->sane.model, "C460", 4) ||
215141cc406Sopenharmony_ci	    !!strstr(dev->sane.model, "CLX-3170") ||
216141cc406Sopenharmony_ci            !!strstr(dev->sane.model, "4x24") ||
217141cc406Sopenharmony_ci            !!strstr(dev->sane.model, "4x28") ||
218141cc406Sopenharmony_ci	    !strncmp(dev->sane.model, "M288x", 5))
219141cc406Sopenharmony_ci	    return 0;
220141cc406Sopenharmony_ci        return 1;
221141cc406Sopenharmony_ci    } else
222141cc406Sopenharmony_ci        return 0;
223141cc406Sopenharmony_ci#else
224141cc406Sopenharmony_ci    return 0;
225141cc406Sopenharmony_ci#endif
226141cc406Sopenharmony_ci}
227141cc406Sopenharmony_ci
228141cc406Sopenharmony_cistatic int isJPEGEnabled(struct device __sane_unused__ *dev)
229141cc406Sopenharmony_ci{
230141cc406Sopenharmony_ci    return isSupportedDevice(dev) && dev->compressionEnabled;
231141cc406Sopenharmony_ci}
232141cc406Sopenharmony_ci
233141cc406Sopenharmony_cistatic void dbg_dump(struct device *dev)
234141cc406Sopenharmony_ci{
235141cc406Sopenharmony_ci    int i;
236141cc406Sopenharmony_ci    char dbuf[MAX_DUMP * 3 + 1], *dptr = dbuf;
237141cc406Sopenharmony_ci    int nzlen = dev->reslen;
238141cc406Sopenharmony_ci    int dlen = MIN(dev->reslen, MAX_DUMP);
239141cc406Sopenharmony_ci
240141cc406Sopenharmony_ci    for (i = dev->reslen - 1; i >= 0; i--, nzlen--)
241141cc406Sopenharmony_ci        if (dev->res[i] != 0)
242141cc406Sopenharmony_ci            break;
243141cc406Sopenharmony_ci
244141cc406Sopenharmony_ci    dlen = MIN(dlen, nzlen + 1);
245141cc406Sopenharmony_ci
246141cc406Sopenharmony_ci    for (i = 0; i < dlen; i++, dptr += 3)
247141cc406Sopenharmony_ci        sprintf(dptr, " %02x", dev->res[i]);
248141cc406Sopenharmony_ci
249141cc406Sopenharmony_ci    DBG(5, "[%lu]%s%s\n", (u_long)dev->reslen, dbuf,
250141cc406Sopenharmony_ci        (dlen < (int)dev->reslen)? "..." : "");
251141cc406Sopenharmony_ci}
252141cc406Sopenharmony_ci
253141cc406Sopenharmony_ci/* one command to device */
254141cc406Sopenharmony_ci/* return 0: on error, 1: success */
255141cc406Sopenharmony_cistatic int dev_command(struct device *dev, SANE_Byte *cmd, size_t reqlen)
256141cc406Sopenharmony_ci{
257141cc406Sopenharmony_ci    SANE_Status status;
258141cc406Sopenharmony_ci    size_t sendlen = cmd[3] + 4;
259141cc406Sopenharmony_ci    SANE_Byte *res = dev->res;
260141cc406Sopenharmony_ci
261141cc406Sopenharmony_ci
262141cc406Sopenharmony_ci    assert(reqlen <= sizeof(dev->res));	/* requested len */
263141cc406Sopenharmony_ci    dev->reslen = sizeof(dev->res);	/* doing full buffer to flush stalled commands */
264141cc406Sopenharmony_ci
265141cc406Sopenharmony_ci    if (cmd[2] == CMD_SET_WINDOW) {
266141cc406Sopenharmony_ci        /* Set Window have wrong packet length, huh. */
267141cc406Sopenharmony_ci        sendlen = 25;
268141cc406Sopenharmony_ci    }
269141cc406Sopenharmony_ci
270141cc406Sopenharmony_ci    if (cmd[2] == CMD_READ_IMAGE) {
271141cc406Sopenharmony_ci        /* Read Image is raw data, don't need to read response */
272141cc406Sopenharmony_ci        res = NULL;
273141cc406Sopenharmony_ci    }
274141cc406Sopenharmony_ci
275141cc406Sopenharmony_ci    dev->state = 0;
276141cc406Sopenharmony_ci    DBG(4, ":: dev_command(%s[%#x], %lu)\n", str_cmd(cmd[2]), cmd[2],
277141cc406Sopenharmony_ci        (u_long)reqlen);
278141cc406Sopenharmony_ci    status = dev->io->dev_request(dev, cmd, sendlen, res, &dev->reslen);
279141cc406Sopenharmony_ci    if (status != SANE_STATUS_GOOD) {
280141cc406Sopenharmony_ci        DBG(1, "%s: dev_request: %s\n", __func__, sane_strstatus(status));
281141cc406Sopenharmony_ci        dev->state = SANE_STATUS_IO_ERROR;
282141cc406Sopenharmony_ci        return 0;
283141cc406Sopenharmony_ci    }
284141cc406Sopenharmony_ci
285141cc406Sopenharmony_ci    if (!res) {
286141cc406Sopenharmony_ci        /* if not need response just return success */
287141cc406Sopenharmony_ci        return 1;
288141cc406Sopenharmony_ci    }
289141cc406Sopenharmony_ci
290141cc406Sopenharmony_ci    /* normal command reply, some sanity checking */
291141cc406Sopenharmony_ci    if (dev->reslen < reqlen) {
292141cc406Sopenharmony_ci        DBG(1, "%s: illegal response len %lu, need %lu\n",
293141cc406Sopenharmony_ci            __func__, (u_long)dev->reslen, (u_long)reqlen);
294141cc406Sopenharmony_ci        dev->state = SANE_STATUS_IO_ERROR;
295141cc406Sopenharmony_ci        return 0;
296141cc406Sopenharmony_ci    } else {
297141cc406Sopenharmony_ci        size_t pktlen;		/* len specified in packet */
298141cc406Sopenharmony_ci
299141cc406Sopenharmony_ci        if (DBG_LEVEL > 3)
300141cc406Sopenharmony_ci            dbg_dump(dev);
301141cc406Sopenharmony_ci
302141cc406Sopenharmony_ci        if (dev->res[0] != RES_CODE) {
303141cc406Sopenharmony_ci            DBG(2, "%s: illegal data header %02x\n", __func__, dev->res[0]);
304141cc406Sopenharmony_ci            dev->state = SANE_STATUS_IO_ERROR;
305141cc406Sopenharmony_ci            return 0;
306141cc406Sopenharmony_ci        }
307141cc406Sopenharmony_ci        pktlen = dev->res[2] + 3;
308141cc406Sopenharmony_ci        if (dev->reslen != pktlen) {
309141cc406Sopenharmony_ci            DBG(2, "%s: illegal response len %lu, should be %lu\n",
310141cc406Sopenharmony_ci                __func__, (u_long)pktlen, (u_long)dev->reslen);
311141cc406Sopenharmony_ci            dev->state = SANE_STATUS_IO_ERROR;
312141cc406Sopenharmony_ci            return 0;
313141cc406Sopenharmony_ci        }
314141cc406Sopenharmony_ci        if (dev->reslen > reqlen)
315141cc406Sopenharmony_ci            DBG(2, "%s: too big packet len %lu, need %lu\n",
316141cc406Sopenharmony_ci                __func__, (u_long)dev->reslen, (u_long)reqlen);
317141cc406Sopenharmony_ci    }
318141cc406Sopenharmony_ci
319141cc406Sopenharmony_ci    dev->state = 0;
320141cc406Sopenharmony_ci    if (cmd[2] == CMD_SET_WINDOW ||
321141cc406Sopenharmony_ci        cmd[2] == CMD_OBJECT_POSITION ||
322141cc406Sopenharmony_ci        cmd[2] == CMD_READ ||
323141cc406Sopenharmony_ci        cmd[2] == CMD_RESERVE_UNIT) {
324141cc406Sopenharmony_ci        if (dev->res[1] == STATUS_BUSY)
325141cc406Sopenharmony_ci            dev->state = SANE_STATUS_DEVICE_BUSY;
326141cc406Sopenharmony_ci        else if (dev->res[1] == STATUS_CANCEL)
327141cc406Sopenharmony_ci            dev->state = SANE_STATUS_CANCELLED;
328141cc406Sopenharmony_ci        else if (dev->res[1] == STATUS_CHECK)
329141cc406Sopenharmony_ci            dev->state = resolv_state((cmd[2] == CMD_READ)?
330141cc406Sopenharmony_ci                                      (dev->res[12] << 8 | dev->res[13]) :
331141cc406Sopenharmony_ci                                      (dev->res[4] << 8 | dev->res[5]));
332141cc406Sopenharmony_ci
333141cc406Sopenharmony_ci        if (dev->state)
334141cc406Sopenharmony_ci            DBG(3, "%s(%s[%#x]): => %d: %s\n",
335141cc406Sopenharmony_ci                __func__, str_cmd(cmd[2]), cmd[2],
336141cc406Sopenharmony_ci                dev->state, sane_strstatus(dev->state));
337141cc406Sopenharmony_ci    }
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci    return 1;
340141cc406Sopenharmony_ci}
341141cc406Sopenharmony_ci
342141cc406Sopenharmony_ci/* one short command to device */
343141cc406Sopenharmony_cistatic int dev_cmd(struct device *dev, SANE_Byte command)
344141cc406Sopenharmony_ci{
345141cc406Sopenharmony_ci    SANE_Byte cmd[4] = { REQ_CODE_A, REQ_CODE_B };
346141cc406Sopenharmony_ci    cmd[2] = command;
347141cc406Sopenharmony_ci    return dev_command(dev, cmd, (command == CMD_INQUIRY)? 70 : 32);
348141cc406Sopenharmony_ci}
349141cc406Sopenharmony_ci
350141cc406Sopenharmony_ci/* stop scanning operation. return previous status */
351141cc406Sopenharmony_cistatic SANE_Status dev_stop(struct device *dev)
352141cc406Sopenharmony_ci{
353141cc406Sopenharmony_ci    int state = dev->state;
354141cc406Sopenharmony_ci
355141cc406Sopenharmony_ci    DBG(3, "%s: %p, scanning %d, reserved %d\n", __func__,
356141cc406Sopenharmony_ci        (void *)dev, dev->scanning, dev->reserved);
357141cc406Sopenharmony_ci    dev->scanning = 0;
358141cc406Sopenharmony_ci
359141cc406Sopenharmony_ci    /* release */
360141cc406Sopenharmony_ci    if (!dev->reserved)
361141cc406Sopenharmony_ci        return state;
362141cc406Sopenharmony_ci    dev->reserved = 0;
363141cc406Sopenharmony_ci    dev_cmd(dev, CMD_RELEASE_UNIT);
364141cc406Sopenharmony_ci    DBG(3, "total image %d*%d size %d (win %d*%d), %d*%d %d data: %d, out %d bytes\n",
365141cc406Sopenharmony_ci        dev->para.pixels_per_line, dev->para.lines,
366141cc406Sopenharmony_ci        dev->total_img_size,
367141cc406Sopenharmony_ci        dev->win_width, dev->win_len,
368141cc406Sopenharmony_ci        dev->pixels_per_line, dev->ulines, dev->blocks,
369141cc406Sopenharmony_ci        dev->total_data_size, dev->total_out_size);
370141cc406Sopenharmony_ci    dev->state = state;
371141cc406Sopenharmony_ci    return state;
372141cc406Sopenharmony_ci}
373141cc406Sopenharmony_ci
374141cc406Sopenharmony_ciSANE_Status ret_cancel(struct device *dev, SANE_Status ret)
375141cc406Sopenharmony_ci{
376141cc406Sopenharmony_ci    dev_cmd(dev, CMD_ABORT);
377141cc406Sopenharmony_ci    if (dev->scanning) {
378141cc406Sopenharmony_ci        dev_stop(dev);
379141cc406Sopenharmony_ci        dev->state = SANE_STATUS_CANCELLED;
380141cc406Sopenharmony_ci    }
381141cc406Sopenharmony_ci    return ret;
382141cc406Sopenharmony_ci}
383141cc406Sopenharmony_ci
384141cc406Sopenharmony_cistatic int cancelled(struct device *dev)
385141cc406Sopenharmony_ci{
386141cc406Sopenharmony_ci    if (dev->cancel)
387141cc406Sopenharmony_ci        return ret_cancel(dev, 1);
388141cc406Sopenharmony_ci    return 0;
389141cc406Sopenharmony_ci}
390141cc406Sopenharmony_ci
391141cc406Sopenharmony_ci/* issue command and wait until scanner is not busy */
392141cc406Sopenharmony_ci/* return 0 on error/blocking, 1 is ok and ready */
393141cc406Sopenharmony_cistatic int dev_cmd_wait(struct device *dev, int cmd)
394141cc406Sopenharmony_ci{
395141cc406Sopenharmony_ci    int sleeptime = 10;
396141cc406Sopenharmony_ci
397141cc406Sopenharmony_ci    do {
398141cc406Sopenharmony_ci        if (cancelled(dev))
399141cc406Sopenharmony_ci            return 0;
400141cc406Sopenharmony_ci        if (!dev_cmd(dev, cmd)) {
401141cc406Sopenharmony_ci            dev->state = SANE_STATUS_IO_ERROR;
402141cc406Sopenharmony_ci            return 0;
403141cc406Sopenharmony_ci        } else if (dev->state) {
404141cc406Sopenharmony_ci            if (dev->state != SANE_STATUS_DEVICE_BUSY)
405141cc406Sopenharmony_ci                return 0;
406141cc406Sopenharmony_ci            else {
407141cc406Sopenharmony_ci                if (dev->non_blocking) {
408141cc406Sopenharmony_ci                    dev->state = SANE_STATUS_GOOD;
409141cc406Sopenharmony_ci                    return 0;
410141cc406Sopenharmony_ci                } else {
411141cc406Sopenharmony_ci                    if (sleeptime > 1000)
412141cc406Sopenharmony_ci                        sleeptime = 1000;
413141cc406Sopenharmony_ci                    DBG(4, "(%s) sleeping(%d ms).. [%x %x]\n",
414141cc406Sopenharmony_ci                        str_cmd(cmd), sleeptime, dev->res[4], dev->res[5]);
415141cc406Sopenharmony_ci                    usleep(sleeptime * 1000);
416141cc406Sopenharmony_ci                    if (sleeptime < 1000)
417141cc406Sopenharmony_ci                        sleeptime *= (sleeptime < 100)? 10 : 2;
418141cc406Sopenharmony_ci                }
419141cc406Sopenharmony_ci            } /* BUSY */
420141cc406Sopenharmony_ci        }
421141cc406Sopenharmony_ci    } while (dev->state == SANE_STATUS_DEVICE_BUSY);
422141cc406Sopenharmony_ci
423141cc406Sopenharmony_ci    return 1;
424141cc406Sopenharmony_ci}
425141cc406Sopenharmony_ci
426141cc406Sopenharmony_cistatic int inq_dpi_bits[] = {
427141cc406Sopenharmony_ci    75, 150, 0, 0,
428141cc406Sopenharmony_ci    200, 300, 0, 0,
429141cc406Sopenharmony_ci    600, 0, 0, 1200,
430141cc406Sopenharmony_ci    100, 0, 0, 2400,
431141cc406Sopenharmony_ci    0, 4800, 0, 9600
432141cc406Sopenharmony_ci};
433141cc406Sopenharmony_ci
434141cc406Sopenharmony_cistatic int res_dpi_codes[] = {
435141cc406Sopenharmony_ci    75, 0, 150, 0,
436141cc406Sopenharmony_ci    0, 300, 0, 600,
437141cc406Sopenharmony_ci    1200, 200, 100, 2400,
438141cc406Sopenharmony_ci    4800, 9600
439141cc406Sopenharmony_ci};
440141cc406Sopenharmony_ci
441141cc406Sopenharmony_cistatic int SANE_Word_sort(const void *a, const void *b)
442141cc406Sopenharmony_ci{
443141cc406Sopenharmony_ci    return *(const SANE_Word *)a - *(const SANE_Word *)b;
444141cc406Sopenharmony_ci}
445141cc406Sopenharmony_ci
446141cc406Sopenharmony_ci/* resolve inquired dpi list to dpi_list array */
447141cc406Sopenharmony_cistatic void resolv_inq_dpi(struct device *dev)
448141cc406Sopenharmony_ci{
449141cc406Sopenharmony_ci    unsigned int i;
450141cc406Sopenharmony_ci    int res = dev->resolutions;
451141cc406Sopenharmony_ci
452141cc406Sopenharmony_ci    assert(sizeof(inq_dpi_bits) < sizeof(dev->dpi_list));
453141cc406Sopenharmony_ci    for (i = 0; i < sizeof(inq_dpi_bits) / sizeof(int); i++)
454141cc406Sopenharmony_ci        if (inq_dpi_bits[i] && (res & (1 << i)))
455141cc406Sopenharmony_ci            dev->dpi_list[++dev->dpi_list[0]] = inq_dpi_bits[i];
456141cc406Sopenharmony_ci    qsort(&dev->dpi_list[1], dev->dpi_list[0], sizeof(SANE_Word), SANE_Word_sort);
457141cc406Sopenharmony_ci}
458141cc406Sopenharmony_ci
459141cc406Sopenharmony_cistatic unsigned int dpi_to_code(int dpi)
460141cc406Sopenharmony_ci{
461141cc406Sopenharmony_ci    unsigned int i;
462141cc406Sopenharmony_ci
463141cc406Sopenharmony_ci    for (i = 0; i < sizeof(res_dpi_codes) / sizeof(int); i++) {
464141cc406Sopenharmony_ci        if (dpi == res_dpi_codes[i])
465141cc406Sopenharmony_ci            return i;
466141cc406Sopenharmony_ci    }
467141cc406Sopenharmony_ci    return 0;
468141cc406Sopenharmony_ci}
469141cc406Sopenharmony_ci
470141cc406Sopenharmony_cistatic int string_match_index(const SANE_String_Const s[], SANE_String m)
471141cc406Sopenharmony_ci{
472141cc406Sopenharmony_ci    int i;
473141cc406Sopenharmony_ci
474141cc406Sopenharmony_ci    for (i = 0; *s; i++) {
475141cc406Sopenharmony_ci        SANE_String_Const x = *s++;
476141cc406Sopenharmony_ci        if (strcasecmp(x, m) == 0)
477141cc406Sopenharmony_ci            return i;
478141cc406Sopenharmony_ci    }
479141cc406Sopenharmony_ci    return 0;
480141cc406Sopenharmony_ci}
481141cc406Sopenharmony_ci
482141cc406Sopenharmony_cistatic SANE_String string_match(const SANE_String_Const s[], SANE_String m)
483141cc406Sopenharmony_ci{
484141cc406Sopenharmony_ci    return UNCONST(s[string_match_index(s, m)]);
485141cc406Sopenharmony_ci}
486141cc406Sopenharmony_ci
487141cc406Sopenharmony_cistatic size_t max_string_size(SANE_String_Const s[])
488141cc406Sopenharmony_ci{
489141cc406Sopenharmony_ci    size_t max = 0;
490141cc406Sopenharmony_ci
491141cc406Sopenharmony_ci    while (*s) {
492141cc406Sopenharmony_ci        size_t size = strlen(*s++) + 1;
493141cc406Sopenharmony_ci        if (size > max)
494141cc406Sopenharmony_ci            max = size;
495141cc406Sopenharmony_ci    }
496141cc406Sopenharmony_ci    return max;
497141cc406Sopenharmony_ci}
498141cc406Sopenharmony_ci
499141cc406Sopenharmony_cistatic SANE_String_Const doc_sources[] = {
500141cc406Sopenharmony_ci    "Flatbed", "ADF", "Auto", NULL
501141cc406Sopenharmony_ci};
502141cc406Sopenharmony_ci
503141cc406Sopenharmony_cistatic int doc_source_to_code[] = {
504141cc406Sopenharmony_ci    0x40, 0x20, 0x80
505141cc406Sopenharmony_ci};
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_cistatic SANE_String_Const scan_modes[] = {
508141cc406Sopenharmony_ci    SANE_VALUE_SCAN_MODE_LINEART,
509141cc406Sopenharmony_ci    SANE_VALUE_SCAN_MODE_HALFTONE,
510141cc406Sopenharmony_ci    SANE_VALUE_SCAN_MODE_GRAY,
511141cc406Sopenharmony_ci    SANE_VALUE_SCAN_MODE_COLOR,
512141cc406Sopenharmony_ci    NULL
513141cc406Sopenharmony_ci};
514141cc406Sopenharmony_ci
515141cc406Sopenharmony_cistatic int scan_mode_to_code[] = {
516141cc406Sopenharmony_ci    0x00, 0x01, 0x03, 0x05
517141cc406Sopenharmony_ci};
518141cc406Sopenharmony_ci
519141cc406Sopenharmony_cistatic SANE_Range threshold = {
520141cc406Sopenharmony_ci    SANE_FIX(30), SANE_FIX(70), SANE_FIX(10)
521141cc406Sopenharmony_ci};
522141cc406Sopenharmony_ci
523141cc406Sopenharmony_cistatic void reset_options(struct device *dev)
524141cc406Sopenharmony_ci{
525141cc406Sopenharmony_ci    dev->val[OPT_RESOLUTION].w = 150;
526141cc406Sopenharmony_ci    dev->val[OPT_MODE].s = string_match(scan_modes, SANE_VALUE_SCAN_MODE_COLOR);
527141cc406Sopenharmony_ci
528141cc406Sopenharmony_ci    /* if docs loaded in adf use it as default source, flatbed otherwise */
529141cc406Sopenharmony_ci    dev->val[OPT_SOURCE].s = UNCONST(doc_sources[(dev->doc_loaded)? 1 : 0]);
530141cc406Sopenharmony_ci
531141cc406Sopenharmony_ci    dev->val[OPT_THRESHOLD].w = SANE_FIX(50);
532141cc406Sopenharmony_ci
533141cc406Sopenharmony_ci    /* this is reported maximum window size, will be fixed later */
534141cc406Sopenharmony_ci    dev->win_x_range.min = SANE_FIX(0);
535141cc406Sopenharmony_ci    dev->win_x_range.max = SANE_FIX((double)dev->max_win_width / PNT_PER_MM);
536141cc406Sopenharmony_ci    dev->win_x_range.quant = SANE_FIX(1);
537141cc406Sopenharmony_ci    dev->win_y_range.min = SANE_FIX(0);
538141cc406Sopenharmony_ci    dev->win_y_range.max = SANE_FIX((double)dev->max_win_len / PNT_PER_MM);
539141cc406Sopenharmony_ci    dev->win_y_range.quant = SANE_FIX(1);
540141cc406Sopenharmony_ci    dev->val[OPT_SCAN_TL_X].w = dev->win_x_range.min;
541141cc406Sopenharmony_ci    dev->val[OPT_SCAN_TL_Y].w = dev->win_y_range.min;
542141cc406Sopenharmony_ci    dev->val[OPT_SCAN_BR_X].w = dev->win_x_range.max;
543141cc406Sopenharmony_ci    dev->val[OPT_SCAN_BR_Y].w = dev->win_y_range.max;
544141cc406Sopenharmony_ci}
545141cc406Sopenharmony_ci
546141cc406Sopenharmony_cistatic void init_options(struct device *dev)
547141cc406Sopenharmony_ci{
548141cc406Sopenharmony_ci    int i;
549141cc406Sopenharmony_ci
550141cc406Sopenharmony_ci    for (i = 0; i < NUM_OPTIONS; i++) {
551141cc406Sopenharmony_ci        dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
552141cc406Sopenharmony_ci        dev->opt[i].size = sizeof(SANE_Word);
553141cc406Sopenharmony_ci        dev->opt[i].type = SANE_TYPE_FIXED;
554141cc406Sopenharmony_ci        dev->val[i].s = NULL;
555141cc406Sopenharmony_ci    }
556141cc406Sopenharmony_ci
557141cc406Sopenharmony_ci    dev->opt[OPT_NUMOPTIONS].name = SANE_NAME_NUM_OPTIONS;
558141cc406Sopenharmony_ci    dev->opt[OPT_NUMOPTIONS].title = SANE_TITLE_NUM_OPTIONS;
559141cc406Sopenharmony_ci    dev->opt[OPT_NUMOPTIONS].desc = SANE_DESC_NUM_OPTIONS;
560141cc406Sopenharmony_ci    dev->opt[OPT_NUMOPTIONS].type = SANE_TYPE_INT;
561141cc406Sopenharmony_ci    dev->opt[OPT_NUMOPTIONS].cap = SANE_CAP_SOFT_DETECT;
562141cc406Sopenharmony_ci    dev->val[OPT_NUMOPTIONS].w = NUM_OPTIONS;
563141cc406Sopenharmony_ci
564141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_STD].name = SANE_NAME_STANDARD;
565141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_STD].title = SANE_TITLE_STANDARD;
566141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_STD].desc = SANE_DESC_STANDARD;
567141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_STD].type = SANE_TYPE_GROUP;
568141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_STD].cap = 0;
569141cc406Sopenharmony_ci
570141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
571141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
572141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
573141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
574141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
575141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
576141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
577141cc406Sopenharmony_ci    dev->opt[OPT_RESOLUTION].constraint.word_list = dev->dpi_list;
578141cc406Sopenharmony_ci
579141cc406Sopenharmony_ci    dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
580141cc406Sopenharmony_ci    dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
581141cc406Sopenharmony_ci    dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
582141cc406Sopenharmony_ci    dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
583141cc406Sopenharmony_ci    dev->opt[OPT_MODE].size = max_string_size(scan_modes);
584141cc406Sopenharmony_ci    dev->opt[OPT_MODE].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
585141cc406Sopenharmony_ci    dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
586141cc406Sopenharmony_ci    dev->opt[OPT_MODE].constraint.string_list = scan_modes;
587141cc406Sopenharmony_ci
588141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].name = SANE_NAME_HIGHLIGHT;
589141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
590141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
591141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
592141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
593141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
594141cc406Sopenharmony_ci    dev->opt[OPT_THRESHOLD].constraint.range = &threshold;
595141cc406Sopenharmony_ci
596141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
597141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
598141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
599141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
600141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].size = max_string_size(doc_sources);
601141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
602141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
603141cc406Sopenharmony_ci    dev->opt[OPT_SOURCE].constraint.string_list = doc_sources;
604141cc406Sopenharmony_ci
605141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].name = "jpeg";
606141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].title = SANE_I18N("jpeg compression");
607141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].desc = SANE_I18N("JPEG Image Compression");
608141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].unit = SANE_UNIT_NONE;
609141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].type = SANE_TYPE_BOOL;
610141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].cap |= SANE_CAP_ADVANCED;
611141cc406Sopenharmony_ci#ifdef HAVE_LIBJPEG
612141cc406Sopenharmony_ci    dev->compressionEnabled = SANE_TRUE;
613141cc406Sopenharmony_ci    if (!isSupportedDevice(dev))
614141cc406Sopenharmony_ci        dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
615141cc406Sopenharmony_ci    dev->val[OPT_JPEG].b = SANE_TRUE;
616141cc406Sopenharmony_ci#else
617141cc406Sopenharmony_ci    dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
618141cc406Sopenharmony_ci    dev->val[OPT_JPEG].b = SANE_FALSE;
619141cc406Sopenharmony_ci#endif
620141cc406Sopenharmony_ci
621141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_GEO].name = SANE_NAME_GEOMETRY;
622141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_GEO].title = SANE_TITLE_GEOMETRY;
623141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_GEO].desc = SANE_DESC_GEOMETRY;
624141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_GEO].type = SANE_TYPE_GROUP;
625141cc406Sopenharmony_ci    dev->opt[OPT_GROUP_GEO].cap = 0;
626141cc406Sopenharmony_ci
627141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_X].name = SANE_NAME_SCAN_TL_X;
628141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_X].title = SANE_TITLE_SCAN_TL_X;
629141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_X].desc = SANE_DESC_SCAN_TL_X;
630141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_X].unit = SANE_UNIT_MM;
631141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
632141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_X].constraint.range = &dev->win_x_range;
633141cc406Sopenharmony_ci
634141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_Y].name = SANE_NAME_SCAN_TL_Y;
635141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
636141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
637141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_Y].unit = SANE_UNIT_MM;
638141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
639141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_TL_Y].constraint.range = &dev->win_y_range;
640141cc406Sopenharmony_ci
641141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_X].name = SANE_NAME_SCAN_BR_X;
642141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_X].title = SANE_TITLE_SCAN_BR_X;
643141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_X].desc = SANE_DESC_SCAN_BR_X;
644141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_X].unit = SANE_UNIT_MM;
645141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
646141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_X].constraint.range = &dev->win_x_range;
647141cc406Sopenharmony_ci
648141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_Y].name = SANE_NAME_SCAN_BR_Y;
649141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
650141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
651141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_Y].unit = SANE_UNIT_MM;
652141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
653141cc406Sopenharmony_ci    dev->opt[OPT_SCAN_BR_Y].constraint.range = &dev->win_y_range;
654141cc406Sopenharmony_ci}
655141cc406Sopenharmony_ci
656141cc406Sopenharmony_ci/* fill parameters from options */
657141cc406Sopenharmony_cistatic void set_parameters(struct device *dev)
658141cc406Sopenharmony_ci{
659141cc406Sopenharmony_ci    double px_to_len;
660141cc406Sopenharmony_ci
661141cc406Sopenharmony_ci    dev->para.last_frame = SANE_TRUE;
662141cc406Sopenharmony_ci    dev->para.lines = -1;
663141cc406Sopenharmony_ci    px_to_len = 1200.0 / dev->val[OPT_RESOLUTION].w;
664141cc406Sopenharmony_ci#define BETTER_BASEDPI 1
665141cc406Sopenharmony_ci    /* tests prove that 1200dpi base is very inexact
666141cc406Sopenharmony_ci     * so I calculated better values for each axis */
667141cc406Sopenharmony_ci#if BETTER_BASEDPI
668141cc406Sopenharmony_ci    px_to_len = 1180.0 / dev->val[OPT_RESOLUTION].w;
669141cc406Sopenharmony_ci#endif
670141cc406Sopenharmony_ci    dev->para.pixels_per_line = dev->win_width / px_to_len;
671141cc406Sopenharmony_ci    dev->para.bytes_per_line = dev->para.pixels_per_line;
672141cc406Sopenharmony_ci
673141cc406Sopenharmony_ci    DBG(5, dev->val[OPT_JPEG].b ? "JPEG compression enabled\n" : "JPEG compression disabled\n" );
674141cc406Sopenharmony_ci    dev->compressionEnabled = dev->val[OPT_JPEG].b;
675141cc406Sopenharmony_ci
676141cc406Sopenharmony_ci    if (!isJPEGEnabled(dev)) {
677141cc406Sopenharmony_ci#if BETTER_BASEDPI
678141cc406Sopenharmony_ci        px_to_len = 1213.9 / dev->val[OPT_RESOLUTION].w;
679141cc406Sopenharmony_ci#endif
680141cc406Sopenharmony_ci    }
681141cc406Sopenharmony_ci    dev->para.lines = dev->win_len / px_to_len;
682141cc406Sopenharmony_ci    if (dev->composition == MODE_LINEART ||
683141cc406Sopenharmony_ci        dev->composition == MODE_HALFTONE) {
684141cc406Sopenharmony_ci        dev->para.format = SANE_FRAME_GRAY;
685141cc406Sopenharmony_ci        dev->para.depth = 1;
686141cc406Sopenharmony_ci        dev->para.bytes_per_line = (dev->para.pixels_per_line + 7) / 8;
687141cc406Sopenharmony_ci    } else if (dev->composition == MODE_GRAY8) {
688141cc406Sopenharmony_ci        dev->para.format = SANE_FRAME_GRAY;
689141cc406Sopenharmony_ci        dev->para.depth = 8;
690141cc406Sopenharmony_ci        dev->para.bytes_per_line = dev->para.pixels_per_line;
691141cc406Sopenharmony_ci    } else if (dev->composition == MODE_RGB24) {
692141cc406Sopenharmony_ci        dev->para.format = SANE_FRAME_RGB;
693141cc406Sopenharmony_ci        dev->para.depth = 8;
694141cc406Sopenharmony_ci        dev->para.bytes_per_line *= 3;
695141cc406Sopenharmony_ci    } else {
696141cc406Sopenharmony_ci        /* this will never happen */
697141cc406Sopenharmony_ci        DBG(1, "%s: impossible image composition %d\n",
698141cc406Sopenharmony_ci            __func__, dev->composition);
699141cc406Sopenharmony_ci        dev->para.format = SANE_FRAME_GRAY;
700141cc406Sopenharmony_ci        dev->para.depth = 8;
701141cc406Sopenharmony_ci    }
702141cc406Sopenharmony_ci}
703141cc406Sopenharmony_ci
704141cc406Sopenharmony_ci/* resolve all options related to scan window */
705141cc406Sopenharmony_ci/* called after option changed and in set_window */
706141cc406Sopenharmony_cistatic int fix_window(struct device *dev)
707141cc406Sopenharmony_ci{
708141cc406Sopenharmony_ci    double win_width_mm, win_len_mm;
709141cc406Sopenharmony_ci    int i;
710141cc406Sopenharmony_ci    int threshold = SANE_UNFIX(dev->val[OPT_THRESHOLD].w);
711141cc406Sopenharmony_ci
712141cc406Sopenharmony_ci    dev->resolution = dpi_to_code(dev->val[OPT_RESOLUTION].w);
713141cc406Sopenharmony_ci    dev->composition = scan_mode_to_code[string_match_index(scan_modes, dev->val[OPT_MODE].s)];
714141cc406Sopenharmony_ci
715141cc406Sopenharmony_ci    if (dev->composition == MODE_LINEART ||
716141cc406Sopenharmony_ci        dev->composition == MODE_HALFTONE) {
717141cc406Sopenharmony_ci        dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
718141cc406Sopenharmony_ci    } else {
719141cc406Sopenharmony_ci        dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
720141cc406Sopenharmony_ci    }
721141cc406Sopenharmony_ci    if (threshold < 30) {
722141cc406Sopenharmony_ci        dev->val[OPT_THRESHOLD].w = SANE_FIX(30);
723141cc406Sopenharmony_ci    } else if (threshold > 70) {
724141cc406Sopenharmony_ci        dev->val[OPT_THRESHOLD].w = SANE_FIX(70);
725141cc406Sopenharmony_ci    }
726141cc406Sopenharmony_ci    threshold = SANE_UNFIX(dev->val[OPT_THRESHOLD].w);
727141cc406Sopenharmony_ci    dev->threshold = (threshold - 30) / 10;
728141cc406Sopenharmony_ci    dev->val[OPT_THRESHOLD].w = SANE_FIX(dev->threshold * 10 + 30);
729141cc406Sopenharmony_ci
730141cc406Sopenharmony_ci    dev->doc_source = doc_source_to_code[string_match_index(doc_sources, dev->val[OPT_SOURCE].s)];
731141cc406Sopenharmony_ci
732141cc406Sopenharmony_ci    /* max window len is dependent of document source */
733141cc406Sopenharmony_ci    if (dev->doc_source == DOC_FLATBED ||
734141cc406Sopenharmony_ci        (dev->doc_source == DOC_AUTO && !dev->doc_loaded))
735141cc406Sopenharmony_ci        dev->max_len = dev->max_len_fb;
736141cc406Sopenharmony_ci    else
737141cc406Sopenharmony_ci        dev->max_len = dev->max_len_adf;
738141cc406Sopenharmony_ci
739141cc406Sopenharmony_ci    /* parameters */
740141cc406Sopenharmony_ci    dev->win_y_range.max = SANE_FIX((double)dev->max_len / PNT_PER_MM);
741141cc406Sopenharmony_ci
742141cc406Sopenharmony_ci    /* window sanity checking */
743141cc406Sopenharmony_ci    for (i = OPT_SCAN_TL_X; i <= OPT_SCAN_BR_Y; i++) {
744141cc406Sopenharmony_ci        if (dev->val[i].w < dev->opt[i].constraint.range->min)
745141cc406Sopenharmony_ci            dev->val[i].w = dev->opt[i].constraint.range->min;
746141cc406Sopenharmony_ci        if (dev->val[i].w > dev->opt[i].constraint.range->max)
747141cc406Sopenharmony_ci            dev->val[i].w = dev->opt[i].constraint.range->max;
748141cc406Sopenharmony_ci    }
749141cc406Sopenharmony_ci
750141cc406Sopenharmony_ci    if (dev->val[OPT_SCAN_TL_X].w > dev->val[OPT_SCAN_BR_X].w)
751141cc406Sopenharmony_ci        SWAP_Word(dev->val[OPT_SCAN_TL_X].w, dev->val[OPT_SCAN_BR_X].w);
752141cc406Sopenharmony_ci    if (dev->val[OPT_SCAN_TL_Y].w > dev->val[OPT_SCAN_BR_Y].w)
753141cc406Sopenharmony_ci        SWAP_Word(dev->val[OPT_SCAN_TL_Y].w, dev->val[OPT_SCAN_BR_Y].w);
754141cc406Sopenharmony_ci
755141cc406Sopenharmony_ci    /* recalculate millimeters to inches */
756141cc406Sopenharmony_ci    dev->win_off_x = SANE_UNFIX(dev->val[OPT_SCAN_TL_X].w) / MM_PER_INCH;
757141cc406Sopenharmony_ci    dev->win_off_y = SANE_UNFIX(dev->val[OPT_SCAN_TL_Y].w) / MM_PER_INCH;
758141cc406Sopenharmony_ci
759141cc406Sopenharmony_ci    /* calc win size in mm */
760141cc406Sopenharmony_ci    win_width_mm = SANE_UNFIX(dev->val[OPT_SCAN_BR_X].w) -
761141cc406Sopenharmony_ci                   SANE_UNFIX(dev->val[OPT_SCAN_TL_X].w);
762141cc406Sopenharmony_ci    win_len_mm = SANE_UNFIX(dev->val[OPT_SCAN_BR_Y].w) -
763141cc406Sopenharmony_ci                 SANE_UNFIX(dev->val[OPT_SCAN_TL_Y].w);
764141cc406Sopenharmony_ci    /* convert mm to 1200 dpi points */
765141cc406Sopenharmony_ci    dev->win_width = (int)(win_width_mm * PNT_PER_MM);
766141cc406Sopenharmony_ci    dev->win_len = (int)(win_len_mm * PNT_PER_MM);
767141cc406Sopenharmony_ci
768141cc406Sopenharmony_ci    /* don't scan if window is zero size */
769141cc406Sopenharmony_ci    if (!dev->win_width || !dev->win_len) {
770141cc406Sopenharmony_ci        /* "The scan cannot be started with the current set of options." */
771141cc406Sopenharmony_ci        dev->state = SANE_STATUS_INVAL;
772141cc406Sopenharmony_ci        return 0;
773141cc406Sopenharmony_ci    }
774141cc406Sopenharmony_ci
775141cc406Sopenharmony_ci    return 1;
776141cc406Sopenharmony_ci}
777141cc406Sopenharmony_ci
778141cc406Sopenharmony_cistatic int dev_set_window(struct device *dev)
779141cc406Sopenharmony_ci{
780141cc406Sopenharmony_ci    SANE_Byte cmd[0x19] = {
781141cc406Sopenharmony_ci        REQ_CODE_A, REQ_CODE_B, CMD_SET_WINDOW, 0x13, MSG_SCANNING_PARAM
782141cc406Sopenharmony_ci    };
783141cc406Sopenharmony_ci
784141cc406Sopenharmony_ci    if (!fix_window(dev))
785141cc406Sopenharmony_ci        return 0;
786141cc406Sopenharmony_ci
787141cc406Sopenharmony_ci    cmd[0x05] = dev->win_width >> 24;
788141cc406Sopenharmony_ci    cmd[0x06] = dev->win_width >> 16;
789141cc406Sopenharmony_ci    cmd[0x07] = dev->win_width >> 8;
790141cc406Sopenharmony_ci    cmd[0x08] = dev->win_width;
791141cc406Sopenharmony_ci    cmd[0x09] = dev->win_len >> 24;
792141cc406Sopenharmony_ci    cmd[0x0a] = dev->win_len >> 16;
793141cc406Sopenharmony_ci    cmd[0x0b] = dev->win_len >> 8;
794141cc406Sopenharmony_ci    cmd[0x0c] = dev->win_len;
795141cc406Sopenharmony_ci    cmd[0x0d] = dev->resolution;		/* x */
796141cc406Sopenharmony_ci    cmd[0x0e] = dev->resolution;		/* y */
797141cc406Sopenharmony_ci    cmd[0x0f] = (SANE_Byte)floor(dev->win_off_x);
798141cc406Sopenharmony_ci    cmd[0x10] = (SANE_Byte)((dev->win_off_x - floor(dev->win_off_x)) * 100);
799141cc406Sopenharmony_ci    cmd[0x11] = (SANE_Byte)floor(dev->win_off_y);
800141cc406Sopenharmony_ci    cmd[0x12] = (SANE_Byte)((dev->win_off_y - floor(dev->win_off_y)) * 100);
801141cc406Sopenharmony_ci    cmd[0x13] = dev->composition;
802141cc406Sopenharmony_ci    /* Set to JPEG Lossy Compression, if mode is color (only for supported model)...
803141cc406Sopenharmony_ci     * else go with Uncompressed (For backard compatibility with old models )*/
804141cc406Sopenharmony_ci    if (dev->composition == MODE_RGB24) {
805141cc406Sopenharmony_ci        if (isJPEGEnabled(dev)) {
806141cc406Sopenharmony_ci            cmd[0x14] = 0x6;
807141cc406Sopenharmony_ci        }
808141cc406Sopenharmony_ci    }
809141cc406Sopenharmony_ci    cmd[0x16] = dev->threshold;
810141cc406Sopenharmony_ci    cmd[0x17] = dev->doc_source;
811141cc406Sopenharmony_ci
812141cc406Sopenharmony_ci    DBG(5, "OFF xi: %02x%02x yi: %02x%02x,"
813141cc406Sopenharmony_ci        " WIN xp: %02x%02x%02x%02x yp %02x%02x%02x%02x,"
814141cc406Sopenharmony_ci        " MAX %08x %08x\n",
815141cc406Sopenharmony_ci        cmd[0x0f], cmd[0x10], cmd[0x11], cmd[0x12],
816141cc406Sopenharmony_ci        cmd[0x05], cmd[0x06], cmd[0x07], cmd[0x08],
817141cc406Sopenharmony_ci        cmd[0x09], cmd[0x0a], cmd[0x0b], cmd[0x0c],
818141cc406Sopenharmony_ci        dev->max_win_width, dev->max_win_len);
819141cc406Sopenharmony_ci
820141cc406Sopenharmony_ci    return dev_command(dev, cmd, 32);
821141cc406Sopenharmony_ci}
822141cc406Sopenharmony_ci
823141cc406Sopenharmony_cistatic SANE_Status
824141cc406Sopenharmony_cidev_inquiry(struct device *dev)
825141cc406Sopenharmony_ci{
826141cc406Sopenharmony_ci    SANE_Byte *ptr;
827141cc406Sopenharmony_ci    SANE_Char *optr, *xptr;
828141cc406Sopenharmony_ci
829141cc406Sopenharmony_ci    if (!dev_cmd(dev, CMD_INQUIRY))
830141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
831141cc406Sopenharmony_ci    ptr = dev->res;
832141cc406Sopenharmony_ci    if (ptr[3] != MSG_PRODUCT_INFO) {
833141cc406Sopenharmony_ci        DBG(1, "%s: illegal INQUIRY response %02x\n", __func__, ptr[3]);
834141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
835141cc406Sopenharmony_ci    }
836141cc406Sopenharmony_ci
837141cc406Sopenharmony_ci    /* parse reported manufacturer/product names */
838141cc406Sopenharmony_ci    dev->sane.vendor = optr = (SANE_Char *) malloc(33);
839141cc406Sopenharmony_ci    for (ptr += 4; ptr < &dev->res[0x24] && *ptr && *ptr != ' ';)
840141cc406Sopenharmony_ci        *optr++ = *ptr++;
841141cc406Sopenharmony_ci    *optr++ = 0;
842141cc406Sopenharmony_ci
843141cc406Sopenharmony_ci    for (; ptr < &dev->res[0x24] && (!*ptr || *ptr == ' '); ptr++)
844141cc406Sopenharmony_ci        /* skip spaces */;
845141cc406Sopenharmony_ci
846141cc406Sopenharmony_ci    dev->sane.model = optr = (SANE_Char *) malloc(33);
847141cc406Sopenharmony_ci    xptr = optr;			/* is last non space character + 1 */
848141cc406Sopenharmony_ci    for (; ptr < &dev->res[0x24] && *ptr;) {
849141cc406Sopenharmony_ci        if (*ptr != ' ')
850141cc406Sopenharmony_ci            xptr = optr + 1;
851141cc406Sopenharmony_ci        *optr++ = *ptr++;
852141cc406Sopenharmony_ci    }
853141cc406Sopenharmony_ci    *optr++ = 0;
854141cc406Sopenharmony_ci    *xptr = 0;
855141cc406Sopenharmony_ci
856141cc406Sopenharmony_ci    DBG(1, "%s: found %s/%s\n", __func__, dev->sane.vendor, dev->sane.model);
857141cc406Sopenharmony_ci    dev->sane.type = strdup("multi-function peripheral");
858141cc406Sopenharmony_ci
859141cc406Sopenharmony_ci    dev->resolutions = dev->res[0x37] << 16 |
860141cc406Sopenharmony_ci                       dev->res[0x24] << 8 |
861141cc406Sopenharmony_ci                       dev->res[0x25];
862141cc406Sopenharmony_ci    dev->compositions = dev->res[0x27];
863141cc406Sopenharmony_ci    dev->max_win_width = dev->res[0x28] << 24 |
864141cc406Sopenharmony_ci                         dev->res[0x29] << 16 |
865141cc406Sopenharmony_ci                         dev->res[0x2a] << 8 |
866141cc406Sopenharmony_ci                         dev->res[0x2b];
867141cc406Sopenharmony_ci    dev->max_win_len = dev->res[0x2c] << 24 |
868141cc406Sopenharmony_ci                       dev->res[0x2d] << 16 |
869141cc406Sopenharmony_ci                       dev->res[0x2e] << 8 |
870141cc406Sopenharmony_ci                       dev->res[0x2f];
871141cc406Sopenharmony_ci    dev->max_len_adf = dev->res[0x38] << 24 |
872141cc406Sopenharmony_ci                       dev->res[0x39] << 16 |
873141cc406Sopenharmony_ci                       dev->res[0x3a] << 8 |
874141cc406Sopenharmony_ci                       dev->res[0x3b];
875141cc406Sopenharmony_ci    dev->max_len_fb = dev->res[0x3c] << 24 |
876141cc406Sopenharmony_ci                      dev->res[0x3d] << 16 |
877141cc406Sopenharmony_ci                      dev->res[0x3e] << 8 |
878141cc406Sopenharmony_ci                      dev->res[0x3f];
879141cc406Sopenharmony_ci    dev->line_order = dev->res[0x31];
880141cc406Sopenharmony_ci    dev->compressionTypes = dev->res[0x32];
881141cc406Sopenharmony_ci    dev->doc_loaded = (dev->res[0x35] == 0x02) &&
882141cc406Sopenharmony_ci                      (dev->res[0x26] & 0x03);
883141cc406Sopenharmony_ci
884141cc406Sopenharmony_ci    init_options(dev);
885141cc406Sopenharmony_ci    reset_options(dev);
886141cc406Sopenharmony_ci    fix_window(dev);
887141cc406Sopenharmony_ci    set_parameters(dev);
888141cc406Sopenharmony_ci    resolv_inq_dpi(dev);
889141cc406Sopenharmony_ci
890141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
891141cc406Sopenharmony_ci}
892141cc406Sopenharmony_ci
893141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
894141cc406Sopenharmony_cisane_get_option_descriptor(SANE_Handle h, SANE_Int opt)
895141cc406Sopenharmony_ci{
896141cc406Sopenharmony_ci    struct device *dev = h;
897141cc406Sopenharmony_ci
898141cc406Sopenharmony_ci    DBG(3, "%s: %p, %d\n", __func__, h, opt);
899141cc406Sopenharmony_ci    if (opt >= NUM_OPTIONS || opt < 0)
900141cc406Sopenharmony_ci        return NULL;
901141cc406Sopenharmony_ci    return &dev->opt[opt];
902141cc406Sopenharmony_ci}
903141cc406Sopenharmony_ci
904141cc406Sopenharmony_ciSANE_Status
905141cc406Sopenharmony_cisane_control_option(SANE_Handle h, SANE_Int opt, SANE_Action act,
906141cc406Sopenharmony_ci                    void *val, SANE_Word *info)
907141cc406Sopenharmony_ci{
908141cc406Sopenharmony_ci    struct device *dev = h;
909141cc406Sopenharmony_ci
910141cc406Sopenharmony_ci    DBG(3, "%s: %p, %d, <%d>, %p, %p\n", __func__, h, opt, act, val, (void *)info);
911141cc406Sopenharmony_ci    if (!dev || opt >= NUM_OPTIONS || opt < 0)
912141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
913141cc406Sopenharmony_ci
914141cc406Sopenharmony_ci    if (info)
915141cc406Sopenharmony_ci        *info = 0;
916141cc406Sopenharmony_ci
917141cc406Sopenharmony_ci    if (act == SANE_ACTION_GET_VALUE) { /* GET */
918141cc406Sopenharmony_ci        if (dev->opt[opt].type == SANE_TYPE_STRING)
919141cc406Sopenharmony_ci            strcpy(val, dev->val[opt].s);
920141cc406Sopenharmony_ci        else
921141cc406Sopenharmony_ci            *(SANE_Word *)val = dev->val[opt].w;
922141cc406Sopenharmony_ci    } else if (act == SANE_ACTION_SET_VALUE) { /* SET */
923141cc406Sopenharmony_ci        SANE_Parameters xpara = dev->para;
924141cc406Sopenharmony_ci        SANE_Option_Descriptor xopt[NUM_OPTIONS];
925141cc406Sopenharmony_ci        Option_Value xval[NUM_OPTIONS];
926141cc406Sopenharmony_ci        int i;
927141cc406Sopenharmony_ci
928141cc406Sopenharmony_ci        if (dev->opt[opt].constraint_type == SANE_CONSTRAINT_STRING_LIST) {
929141cc406Sopenharmony_ci            dev->val[opt].s = string_match(dev->opt[opt].constraint.string_list, val);
930141cc406Sopenharmony_ci            if (info && strcasecmp(dev->val[opt].s, val))
931141cc406Sopenharmony_ci                *info |= SANE_INFO_INEXACT;
932141cc406Sopenharmony_ci        } else if (opt == OPT_RESOLUTION)
933141cc406Sopenharmony_ci            dev->val[opt].w = res_dpi_codes[dpi_to_code(*(SANE_Word *)val)];
934141cc406Sopenharmony_ci        else
935141cc406Sopenharmony_ci            dev->val[opt].w = *(SANE_Word *)val;
936141cc406Sopenharmony_ci
937141cc406Sopenharmony_ci        memcpy(&xopt, &dev->opt, sizeof(xopt));
938141cc406Sopenharmony_ci        memcpy(&xval, &dev->val, sizeof(xval));
939141cc406Sopenharmony_ci        fix_window(dev);
940141cc406Sopenharmony_ci        set_parameters(dev);
941141cc406Sopenharmony_ci
942141cc406Sopenharmony_ci        /* check for side effects */
943141cc406Sopenharmony_ci        if (info) {
944141cc406Sopenharmony_ci            if (memcmp(&xpara, &dev->para, sizeof(xpara)))
945141cc406Sopenharmony_ci                *info |= SANE_INFO_RELOAD_PARAMS;
946141cc406Sopenharmony_ci            if (memcmp(&xopt, &dev->opt, sizeof(xopt)))
947141cc406Sopenharmony_ci                *info |= SANE_INFO_RELOAD_OPTIONS;
948141cc406Sopenharmony_ci            for (i = 0; i < NUM_OPTIONS; i++)
949141cc406Sopenharmony_ci                if (xval[i].w != dev->val[i].w) {
950141cc406Sopenharmony_ci                    if (i == opt)
951141cc406Sopenharmony_ci                        *info |= SANE_INFO_INEXACT;
952141cc406Sopenharmony_ci                    else
953141cc406Sopenharmony_ci                        *info |= SANE_INFO_RELOAD_OPTIONS;
954141cc406Sopenharmony_ci                }
955141cc406Sopenharmony_ci        }
956141cc406Sopenharmony_ci    }
957141cc406Sopenharmony_ci
958141cc406Sopenharmony_ci    DBG(4, "%s: %d, <%d> => %08x, %x\n", __func__, opt, act,
959141cc406Sopenharmony_ci        val? *(SANE_Word *)val : 0, info? *info : 0);
960141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
961141cc406Sopenharmony_ci}
962141cc406Sopenharmony_ci
963141cc406Sopenharmony_cistatic void
964141cc406Sopenharmony_cidev_free(struct device *dev)
965141cc406Sopenharmony_ci{
966141cc406Sopenharmony_ci    if (!dev)
967141cc406Sopenharmony_ci        return;
968141cc406Sopenharmony_ci
969141cc406Sopenharmony_ci    if (dev->sane.name)
970141cc406Sopenharmony_ci        free(UNCONST(dev->sane.name));
971141cc406Sopenharmony_ci    if (dev->sane.vendor)
972141cc406Sopenharmony_ci        free(UNCONST(dev->sane.vendor));
973141cc406Sopenharmony_ci    if (dev->sane.model)
974141cc406Sopenharmony_ci        free(UNCONST(dev->sane.model));
975141cc406Sopenharmony_ci    if (dev->sane.type)
976141cc406Sopenharmony_ci        free(UNCONST(dev->sane.type));
977141cc406Sopenharmony_ci    if (dev->data)
978141cc406Sopenharmony_ci        free(dev->data);
979141cc406Sopenharmony_ci    if (dev->decData) {
980141cc406Sopenharmony_ci        free(dev->decData);
981141cc406Sopenharmony_ci        dev->decData = NULL;
982141cc406Sopenharmony_ci    }
983141cc406Sopenharmony_ci    memset(dev, 0, sizeof(*dev));
984141cc406Sopenharmony_ci    free(dev);
985141cc406Sopenharmony_ci}
986141cc406Sopenharmony_ci
987141cc406Sopenharmony_cistatic void
988141cc406Sopenharmony_cifree_devices(void)
989141cc406Sopenharmony_ci{
990141cc406Sopenharmony_ci    struct device *next;
991141cc406Sopenharmony_ci    struct device *dev;
992141cc406Sopenharmony_ci
993141cc406Sopenharmony_ci    if (devlist) {
994141cc406Sopenharmony_ci        free(devlist);
995141cc406Sopenharmony_ci        devlist = NULL;
996141cc406Sopenharmony_ci    }
997141cc406Sopenharmony_ci    for (dev = devices_head; dev; dev = next) {
998141cc406Sopenharmony_ci        next = dev->next;
999141cc406Sopenharmony_ci        dev_free(dev);
1000141cc406Sopenharmony_ci    }
1001141cc406Sopenharmony_ci    devices_head = NULL;
1002141cc406Sopenharmony_ci}
1003141cc406Sopenharmony_ci
1004141cc406Sopenharmony_cistatic transport *tr_from_devname(SANE_String_Const devname)
1005141cc406Sopenharmony_ci{
1006141cc406Sopenharmony_ci    if (strncmp("tcp", devname, 3) == 0)
1007141cc406Sopenharmony_ci        return &available_transports[TRANSPORT_TCP];
1008141cc406Sopenharmony_ci    return &available_transports[TRANSPORT_USB];
1009141cc406Sopenharmony_ci}
1010141cc406Sopenharmony_ci
1011141cc406Sopenharmony_cistatic SANE_Status
1012141cc406Sopenharmony_cilist_one_device(SANE_String_Const devname)
1013141cc406Sopenharmony_ci{
1014141cc406Sopenharmony_ci    struct device *dev;
1015141cc406Sopenharmony_ci    SANE_Status status;
1016141cc406Sopenharmony_ci    transport *tr;
1017141cc406Sopenharmony_ci
1018141cc406Sopenharmony_ci    DBG(4, "%s: %s\n", __func__, devname);
1019141cc406Sopenharmony_ci
1020141cc406Sopenharmony_ci    for (dev = devices_head; dev; dev = dev->next) {
1021141cc406Sopenharmony_ci        if (strcmp(dev->sane.name, devname) == 0)
1022141cc406Sopenharmony_ci            return SANE_STATUS_GOOD;
1023141cc406Sopenharmony_ci    }
1024141cc406Sopenharmony_ci
1025141cc406Sopenharmony_ci    tr = tr_from_devname(devname);
1026141cc406Sopenharmony_ci
1027141cc406Sopenharmony_ci    dev = calloc(1, sizeof(struct device));
1028141cc406Sopenharmony_ci    if (dev == NULL)
1029141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
1030141cc406Sopenharmony_ci
1031141cc406Sopenharmony_ci    dev->sane.name = strdup(devname);
1032141cc406Sopenharmony_ci    dev->io = tr;
1033141cc406Sopenharmony_ci    status = tr->dev_open(dev);
1034141cc406Sopenharmony_ci    if (status != SANE_STATUS_GOOD) {
1035141cc406Sopenharmony_ci        dev_free(dev);
1036141cc406Sopenharmony_ci        return status;
1037141cc406Sopenharmony_ci    }
1038141cc406Sopenharmony_ci
1039141cc406Sopenharmony_ci    /*  status = dev_cmd (dev, CMD_ABORT);*/
1040141cc406Sopenharmony_ci    status = dev_inquiry(dev);
1041141cc406Sopenharmony_ci    tr->dev_close(dev);
1042141cc406Sopenharmony_ci    if (status != SANE_STATUS_GOOD) {
1043141cc406Sopenharmony_ci        DBG(1, "%s: dev_inquiry(%s): %s\n", __func__,
1044141cc406Sopenharmony_ci            dev->sane.name, sane_strstatus(status));
1045141cc406Sopenharmony_ci        dev_free(dev);
1046141cc406Sopenharmony_ci        return status;
1047141cc406Sopenharmony_ci    }
1048141cc406Sopenharmony_ci
1049141cc406Sopenharmony_ci    /* good device, add it to list */
1050141cc406Sopenharmony_ci    dev->next = devices_head;
1051141cc406Sopenharmony_ci    devices_head = dev;
1052141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1053141cc406Sopenharmony_ci}
1054141cc406Sopenharmony_ci
1055141cc406Sopenharmony_ci/* SANE API ignores return code of this callback */
1056141cc406Sopenharmony_cistatic SANE_Status
1057141cc406Sopenharmony_cilist_conf_devices(SANEI_Config __sane_unused__ *config, const char *devname,
1058141cc406Sopenharmony_ci                  void __sane_unused__ *data)
1059141cc406Sopenharmony_ci{
1060141cc406Sopenharmony_ci    return tr_from_devname(devname)->configure_device(devname, list_one_device);
1061141cc406Sopenharmony_ci}
1062141cc406Sopenharmony_ci
1063141cc406Sopenharmony_ciSANE_Status
1064141cc406Sopenharmony_cisane_init(SANE_Int *version_code, SANE_Auth_Callback cb)
1065141cc406Sopenharmony_ci{
1066141cc406Sopenharmony_ci    DBG_INIT();
1067141cc406Sopenharmony_ci    DBG(2, "sane_init: Xerox backend (build %d), version %s null, authorize %s null\n", BACKEND_BUILD,
1068141cc406Sopenharmony_ci        (version_code) ? "!=" : "==", (cb) ? "!=" : "==");
1069141cc406Sopenharmony_ci
1070141cc406Sopenharmony_ci    if (version_code)
1071141cc406Sopenharmony_ci        *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BACKEND_BUILD);
1072141cc406Sopenharmony_ci
1073141cc406Sopenharmony_ci    sanei_usb_init();
1074141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1075141cc406Sopenharmony_ci}
1076141cc406Sopenharmony_ci
1077141cc406Sopenharmony_civoid
1078141cc406Sopenharmony_cisane_exit(void)
1079141cc406Sopenharmony_ci{
1080141cc406Sopenharmony_ci    struct device *dev;
1081141cc406Sopenharmony_ci
1082141cc406Sopenharmony_ci    for (dev = devices_head; dev; dev = dev->next)
1083141cc406Sopenharmony_ci        if (dev->dn != -1)
1084141cc406Sopenharmony_ci            sane_close(dev); /* implies flush */
1085141cc406Sopenharmony_ci
1086141cc406Sopenharmony_ci    free_devices();
1087141cc406Sopenharmony_ci}
1088141cc406Sopenharmony_ci
1089141cc406Sopenharmony_ciSANE_Status
1090141cc406Sopenharmony_cisane_get_devices(const SANE_Device *** device_list, SANE_Bool local)
1091141cc406Sopenharmony_ci{
1092141cc406Sopenharmony_ci    SANEI_Config config;
1093141cc406Sopenharmony_ci    struct device *dev;
1094141cc406Sopenharmony_ci    int dev_count;
1095141cc406Sopenharmony_ci    int i;
1096141cc406Sopenharmony_ci
1097141cc406Sopenharmony_ci    DBG(3, "%s: %p, %d\n", __func__, (const void *)device_list, local);
1098141cc406Sopenharmony_ci
1099141cc406Sopenharmony_ci    if (devlist) {
1100141cc406Sopenharmony_ci        if (device_list)
1101141cc406Sopenharmony_ci            *device_list = devlist;
1102141cc406Sopenharmony_ci        return SANE_STATUS_GOOD;
1103141cc406Sopenharmony_ci    }
1104141cc406Sopenharmony_ci
1105141cc406Sopenharmony_ci    free_devices();
1106141cc406Sopenharmony_ci
1107141cc406Sopenharmony_ci    config.count = 0;
1108141cc406Sopenharmony_ci    config.descriptors = NULL;
1109141cc406Sopenharmony_ci    config.values = NULL;
1110141cc406Sopenharmony_ci    sanei_configure_attach(XEROX_CONFIG_FILE, &config, list_conf_devices, NULL);
1111141cc406Sopenharmony_ci
1112141cc406Sopenharmony_ci    for (dev_count = 0, dev = devices_head; dev; dev = dev->next)
1113141cc406Sopenharmony_ci        dev_count++;
1114141cc406Sopenharmony_ci
1115141cc406Sopenharmony_ci    devlist = malloc((dev_count + 1) * sizeof(*devlist));
1116141cc406Sopenharmony_ci    if (!devlist) {
1117141cc406Sopenharmony_ci        DBG(1, "%s: malloc: no memory\n", __func__);
1118141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
1119141cc406Sopenharmony_ci    }
1120141cc406Sopenharmony_ci
1121141cc406Sopenharmony_ci    for (i = 0, dev = devices_head; dev; dev = dev->next)
1122141cc406Sopenharmony_ci        devlist[i++] = &dev->sane;
1123141cc406Sopenharmony_ci    devlist[i++] = NULL;
1124141cc406Sopenharmony_ci
1125141cc406Sopenharmony_ci    if (device_list)
1126141cc406Sopenharmony_ci        *device_list = devlist;
1127141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1128141cc406Sopenharmony_ci}
1129141cc406Sopenharmony_ci
1130141cc406Sopenharmony_civoid
1131141cc406Sopenharmony_cisane_close(SANE_Handle h)
1132141cc406Sopenharmony_ci{
1133141cc406Sopenharmony_ci    struct device *dev = h;
1134141cc406Sopenharmony_ci
1135141cc406Sopenharmony_ci    if (!dev)
1136141cc406Sopenharmony_ci        return;
1137141cc406Sopenharmony_ci
1138141cc406Sopenharmony_ci    DBG(3, "%s: %p (%s)\n", __func__, (void *)dev, dev->sane.name);
1139141cc406Sopenharmony_ci    dev->io->dev_close(dev);
1140141cc406Sopenharmony_ci}
1141141cc406Sopenharmony_ci
1142141cc406Sopenharmony_ciSANE_Status
1143141cc406Sopenharmony_cisane_open(SANE_String_Const name, SANE_Handle *h)
1144141cc406Sopenharmony_ci{
1145141cc406Sopenharmony_ci    struct device *dev;
1146141cc406Sopenharmony_ci
1147141cc406Sopenharmony_ci    DBG(3, "%s: '%s'\n", __func__, name);
1148141cc406Sopenharmony_ci
1149141cc406Sopenharmony_ci    if (!devlist)
1150141cc406Sopenharmony_ci        sane_get_devices(NULL, SANE_TRUE);
1151141cc406Sopenharmony_ci
1152141cc406Sopenharmony_ci    if (!name || !*name) {
1153141cc406Sopenharmony_ci        /* special case of empty name: open first available device */
1154141cc406Sopenharmony_ci        for (dev = devices_head; dev; dev = dev->next) {
1155141cc406Sopenharmony_ci            if (dev->dn != -1) {
1156141cc406Sopenharmony_ci                if (sane_open(dev->sane.name, h) == SANE_STATUS_GOOD)
1157141cc406Sopenharmony_ci                    return SANE_STATUS_GOOD;
1158141cc406Sopenharmony_ci            }
1159141cc406Sopenharmony_ci        }
1160141cc406Sopenharmony_ci    } else {
1161141cc406Sopenharmony_ci        for (dev = devices_head; dev; dev = dev->next) {
1162141cc406Sopenharmony_ci            if (strcmp(name, dev->sane.name) == 0) {
1163141cc406Sopenharmony_ci                *h = dev;
1164141cc406Sopenharmony_ci                return dev->io->dev_open(dev);
1165141cc406Sopenharmony_ci            }
1166141cc406Sopenharmony_ci        }
1167141cc406Sopenharmony_ci    }
1168141cc406Sopenharmony_ci
1169141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1170141cc406Sopenharmony_ci}
1171141cc406Sopenharmony_ci
1172141cc406Sopenharmony_ciSANE_Status
1173141cc406Sopenharmony_cisane_get_parameters(SANE_Handle h, SANE_Parameters *para)
1174141cc406Sopenharmony_ci{
1175141cc406Sopenharmony_ci    struct device *dev = h;
1176141cc406Sopenharmony_ci
1177141cc406Sopenharmony_ci    DBG(3, "%s: %p, %p\n", __func__, h, (void *)para);
1178141cc406Sopenharmony_ci    if (!para)
1179141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1180141cc406Sopenharmony_ci
1181141cc406Sopenharmony_ci    *para = dev->para;
1182141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1183141cc406Sopenharmony_ci}
1184141cc406Sopenharmony_ci
1185141cc406Sopenharmony_ci/* check if image data is ready, and wait if not */
1186141cc406Sopenharmony_ci/* 1: image is acquired, 0: error or non_blocking mode */
1187141cc406Sopenharmony_cistatic int dev_acquire(struct device *dev)
1188141cc406Sopenharmony_ci{
1189141cc406Sopenharmony_ci    if (!dev_cmd_wait(dev, CMD_READ))
1190141cc406Sopenharmony_ci        return 0;
1191141cc406Sopenharmony_ci
1192141cc406Sopenharmony_ci    dev->state = SANE_STATUS_GOOD;
1193141cc406Sopenharmony_ci    dev->vertical = dev->res[0x08] << 8 | dev->res[0x09];
1194141cc406Sopenharmony_ci    dev->horizontal = dev->res[0x0a] << 8 | dev->res[0x0b];
1195141cc406Sopenharmony_ci    dev->blocklen = dev->res[4] << 24 |
1196141cc406Sopenharmony_ci                    dev->res[5] << 16 |
1197141cc406Sopenharmony_ci                    dev->res[6] << 8 |
1198141cc406Sopenharmony_ci                    dev->res[7];
1199141cc406Sopenharmony_ci    dev->final_block = (dev->res[3] == MSG_END_BLOCK)? 1 : 0;
1200141cc406Sopenharmony_ci
1201141cc406Sopenharmony_ci    dev->pixels_per_line = dev->horizontal;
1202141cc406Sopenharmony_ci    dev->bytes_per_line = dev->horizontal;
1203141cc406Sopenharmony_ci
1204141cc406Sopenharmony_ci    if (dev->composition == MODE_RGB24)
1205141cc406Sopenharmony_ci        dev->bytes_per_line *= 3;
1206141cc406Sopenharmony_ci    else if (dev->composition == MODE_LINEART ||
1207141cc406Sopenharmony_ci             dev->composition == MODE_HALFTONE)
1208141cc406Sopenharmony_ci        dev->pixels_per_line *= 8;
1209141cc406Sopenharmony_ci
1210141cc406Sopenharmony_ci    DBG(4, "acquiring, size per band v: %d, h: %d, %sblock: %d, slack: %d\n",
1211141cc406Sopenharmony_ci        dev->vertical, dev->horizontal, dev->final_block? "last " : "",
1212141cc406Sopenharmony_ci        dev->blocklen, dev->blocklen - (dev->vertical * dev->bytes_per_line));
1213141cc406Sopenharmony_ci
1214141cc406Sopenharmony_ci    if (dev->bytes_per_line > DATASIZE) {
1215141cc406Sopenharmony_ci        DBG(1, "%s: unsupported line size: %d bytes > %d\n",
1216141cc406Sopenharmony_ci            __func__, dev->bytes_per_line, DATASIZE);
1217141cc406Sopenharmony_ci        ret_cancel(dev, SANE_STATUS_NO_MEM);
1218141cc406Sopenharmony_ci        return 0;
1219141cc406Sopenharmony_ci    }
1220141cc406Sopenharmony_ci
1221141cc406Sopenharmony_ci    dev->reading = 0; /* need to issue READ_IMAGE */
1222141cc406Sopenharmony_ci
1223141cc406Sopenharmony_ci    dev->dataindex = 0;
1224141cc406Sopenharmony_ci    dev->datalen = 0;
1225141cc406Sopenharmony_ci    dev->dataoff = 0;
1226141cc406Sopenharmony_ci
1227141cc406Sopenharmony_ci    return 1;
1228141cc406Sopenharmony_ci}
1229141cc406Sopenharmony_ci
1230141cc406Sopenharmony_cistatic int fill_slack(struct device *dev, SANE_Byte *buf, int maxlen)
1231141cc406Sopenharmony_ci{
1232141cc406Sopenharmony_ci    const int slack = dev->total_img_size - dev->total_out_size;
1233141cc406Sopenharmony_ci    const int havelen = MIN(slack, maxlen);
1234141cc406Sopenharmony_ci    int j;
1235141cc406Sopenharmony_ci
1236141cc406Sopenharmony_ci    if (havelen <= 0)
1237141cc406Sopenharmony_ci        return 0;
1238141cc406Sopenharmony_ci    for (j = 0; j < havelen; j++)
1239141cc406Sopenharmony_ci        buf[j] = 255;
1240141cc406Sopenharmony_ci    return havelen;
1241141cc406Sopenharmony_ci}
1242141cc406Sopenharmony_ci
1243141cc406Sopenharmony_cistatic int copy_plain_trim(struct device *dev, SANE_Byte *buf, int maxlen, int *olenp)
1244141cc406Sopenharmony_ci{
1245141cc406Sopenharmony_ci    int j;
1246141cc406Sopenharmony_ci    const int linesize = dev->bytes_per_line;
1247141cc406Sopenharmony_ci    int k = dev->dataindex;
1248141cc406Sopenharmony_ci    *olenp = 0;
1249141cc406Sopenharmony_ci    for (j = 0; j < dev->datalen && *olenp < maxlen; j++, k++) {
1250141cc406Sopenharmony_ci        const int x = k % linesize;
1251141cc406Sopenharmony_ci        const int y = k / linesize;
1252141cc406Sopenharmony_ci        if (y >= dev->vertical)
1253141cc406Sopenharmony_ci            break; /* slack */
1254141cc406Sopenharmony_ci        if (x < dev->para.bytes_per_line &&
1255141cc406Sopenharmony_ci            (y + dev->y_off) < dev->para.lines) {
1256141cc406Sopenharmony_ci            *buf++ = dev->data[(dev->dataoff + j) & DATAMASK];
1257141cc406Sopenharmony_ci            (*olenp)++;
1258141cc406Sopenharmony_ci        }
1259141cc406Sopenharmony_ci    }
1260141cc406Sopenharmony_ci    dev->dataindex = k;
1261141cc406Sopenharmony_ci    return j;
1262141cc406Sopenharmony_ci}
1263141cc406Sopenharmony_ci
1264141cc406Sopenharmony_ci/* return: how much data could be freed from cyclic buffer */
1265141cc406Sopenharmony_ci/* convert from RRGGBB to RGBRGB */
1266141cc406Sopenharmony_cistatic int copy_mix_bands_trim(struct device *dev, SANE_Byte *buf, int maxlen, int *olenp)
1267141cc406Sopenharmony_ci{
1268141cc406Sopenharmony_ci    int j;
1269141cc406Sopenharmony_ci
1270141cc406Sopenharmony_ci    const int linesize = dev->bytes_per_line; /* caching real line size */
1271141cc406Sopenharmony_ci
1272141cc406Sopenharmony_ci    /* line number of the head of input buffer,
1273141cc406Sopenharmony_ci     * input buffer is always aligned to whole line */
1274141cc406Sopenharmony_ci    const int y_off = dev->dataindex / linesize;
1275141cc406Sopenharmony_ci
1276141cc406Sopenharmony_ci    int k = dev->dataindex; /* caching current index of input buffer */
1277141cc406Sopenharmony_ci
1278141cc406Sopenharmony_ci    /* can only copy as much as full lines we have */
1279141cc406Sopenharmony_ci    int havelen = dev->datalen / linesize * linesize - k % linesize;
1280141cc406Sopenharmony_ci
1281141cc406Sopenharmony_ci    const int bands = 3;
1282141cc406Sopenharmony_ci    *olenp = 0;
1283141cc406Sopenharmony_ci
1284141cc406Sopenharmony_ci    /* while we have data && they can receive */
1285141cc406Sopenharmony_ci    for (j = 0; j < havelen && *olenp < maxlen; j++, k++) {
1286141cc406Sopenharmony_ci        const int band = (k % bands) * dev->horizontal;
1287141cc406Sopenharmony_ci        const int x = k % linesize / bands;
1288141cc406Sopenharmony_ci        const int y = k / linesize - y_off; /* y relative to buffer head */
1289141cc406Sopenharmony_ci        const int y_rly = y + y_off + dev->y_off; /* global y */
1290141cc406Sopenharmony_ci
1291141cc406Sopenharmony_ci        if (x < dev->para.pixels_per_line &&
1292141cc406Sopenharmony_ci            y_rly < dev->para.lines) {
1293141cc406Sopenharmony_ci            *buf++ = dev->data[(dev->dataoff + band + x + y * linesize) & DATAMASK];
1294141cc406Sopenharmony_ci            (*olenp)++;
1295141cc406Sopenharmony_ci        }
1296141cc406Sopenharmony_ci    }
1297141cc406Sopenharmony_ci    dev->dataindex = k;
1298141cc406Sopenharmony_ci
1299141cc406Sopenharmony_ci    /* how much full lines are finished */
1300141cc406Sopenharmony_ci    return (k / linesize - y_off) * linesize;
1301141cc406Sopenharmony_ci}
1302141cc406Sopenharmony_ci
1303141cc406Sopenharmony_ciSANE_Status
1304141cc406Sopenharmony_cisane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
1305141cc406Sopenharmony_ci{
1306141cc406Sopenharmony_ci    SANE_Status status;
1307141cc406Sopenharmony_ci    struct device *dev = h;
1308141cc406Sopenharmony_ci
1309141cc406Sopenharmony_ci    DBG(3, "%s: %p, %p, %d, %p\n", __func__, h, (void *) buf, maxlen, (void *) lenp);
1310141cc406Sopenharmony_ci
1311141cc406Sopenharmony_ci    if (lenp)
1312141cc406Sopenharmony_ci        *lenp = 0;
1313141cc406Sopenharmony_ci    if (!dev)
1314141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1315141cc406Sopenharmony_ci
1316141cc406Sopenharmony_ci    if (!dev->scanning)
1317141cc406Sopenharmony_ci        return SANE_STATUS_EOF;
1318141cc406Sopenharmony_ci
1319141cc406Sopenharmony_ci    /* if there is no data to read or output from buffer */
1320141cc406Sopenharmony_ci    if (!dev->blocklen && dev->datalen <= PADDING_SIZE) {
1321141cc406Sopenharmony_ci
1322141cc406Sopenharmony_ci        /* copying uncompressed data */
1323141cc406Sopenharmony_ci        if (dev->composition == MODE_RGB24 &&
1324141cc406Sopenharmony_ci            isJPEGEnabled(dev) &&
1325141cc406Sopenharmony_ci            dev->decDataSize > 0) {
1326141cc406Sopenharmony_ci            int diff = dev->total_img_size - dev->total_out_size;
1327141cc406Sopenharmony_ci            int bufLen = (diff < maxlen) ? diff : maxlen;
1328141cc406Sopenharmony_ci            if (diff &&
1329141cc406Sopenharmony_ci                copy_decompress_data(dev, buf, bufLen, lenp)) {
1330141cc406Sopenharmony_ci		if (lenp)
1331141cc406Sopenharmony_ci		    dev->total_out_size += *lenp;
1332141cc406Sopenharmony_ci                return SANE_STATUS_GOOD;
1333141cc406Sopenharmony_ci            }
1334141cc406Sopenharmony_ci        }
1335141cc406Sopenharmony_ci
1336141cc406Sopenharmony_ci        /* and we don't need to acquire next block */
1337141cc406Sopenharmony_ci        if (dev->final_block) {
1338141cc406Sopenharmony_ci            int slack = dev->total_img_size - dev->total_out_size;
1339141cc406Sopenharmony_ci
1340141cc406Sopenharmony_ci            /* but we may need to fill slack */
1341141cc406Sopenharmony_ci            if (buf && lenp && slack > 0) {
1342141cc406Sopenharmony_ci                *lenp = fill_slack(dev, buf, maxlen);
1343141cc406Sopenharmony_ci                dev->total_out_size += *lenp;
1344141cc406Sopenharmony_ci                DBG(9, "<> slack: %d, filled: %d, maxlen %d\n",
1345141cc406Sopenharmony_ci                    slack, *lenp, maxlen);
1346141cc406Sopenharmony_ci                return SANE_STATUS_GOOD;
1347141cc406Sopenharmony_ci            } else if (slack < 0) {
1348141cc406Sopenharmony_ci                /* this will never happen */
1349141cc406Sopenharmony_ci                DBG(1, "image overflow %d bytes\n", dev->total_img_size - dev->total_out_size);
1350141cc406Sopenharmony_ci            }
1351141cc406Sopenharmony_ci            if (isJPEGEnabled(dev) &&
1352141cc406Sopenharmony_ci                dev->composition == MODE_RGB24) {
1353141cc406Sopenharmony_ci                remove(encTmpFileName);
1354141cc406Sopenharmony_ci            }
1355141cc406Sopenharmony_ci            /* that's all */
1356141cc406Sopenharmony_ci            dev_stop(dev);
1357141cc406Sopenharmony_ci            return SANE_STATUS_EOF;
1358141cc406Sopenharmony_ci        }
1359141cc406Sopenharmony_ci
1360141cc406Sopenharmony_ci        /* queue next image block */
1361141cc406Sopenharmony_ci        if (!dev_acquire(dev))
1362141cc406Sopenharmony_ci            return dev->state;
1363141cc406Sopenharmony_ci    }
1364141cc406Sopenharmony_ci
1365141cc406Sopenharmony_ci    if (!dev->reading) {
1366141cc406Sopenharmony_ci        if (cancelled(dev))
1367141cc406Sopenharmony_ci            return dev->state;
1368141cc406Sopenharmony_ci        DBG(5, "READ_IMAGE\n");
1369141cc406Sopenharmony_ci        if (!dev_cmd(dev, CMD_READ_IMAGE))
1370141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
1371141cc406Sopenharmony_ci        dev->reading++;
1372141cc406Sopenharmony_ci        dev->ulines += dev->vertical;
1373141cc406Sopenharmony_ci        dev->y_off = dev->ulines - dev->vertical;
1374141cc406Sopenharmony_ci        dev->total_data_size += dev->blocklen;
1375141cc406Sopenharmony_ci        dev->blocks++;
1376141cc406Sopenharmony_ci    }
1377141cc406Sopenharmony_ci
1378141cc406Sopenharmony_ci    do {
1379141cc406Sopenharmony_ci        size_t datalen;
1380141cc406Sopenharmony_ci        int clrlen; /* cleared lines len */
1381141cc406Sopenharmony_ci        int olen; /* output len */
1382141cc406Sopenharmony_ci
1383141cc406Sopenharmony_ci        /* read as much data into the buffer */
1384141cc406Sopenharmony_ci        datalen = DATAROOM(dev) & USB_BLOCK_MASK;
1385141cc406Sopenharmony_ci        while (datalen && dev->blocklen) {
1386141cc406Sopenharmony_ci            SANE_Byte *rbuf = dev->data + DATATAIL(dev);
1387141cc406Sopenharmony_ci
1388141cc406Sopenharmony_ci            DBG(9, "<> request len: %lu, [%d, %d; %d]\n",
1389141cc406Sopenharmony_ci                (u_long)datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
1390141cc406Sopenharmony_ci            if ((status = dev->io->dev_request(dev, NULL, 0, rbuf, &datalen)) !=
1391141cc406Sopenharmony_ci                SANE_STATUS_GOOD)
1392141cc406Sopenharmony_ci                return status;
1393141cc406Sopenharmony_ci            dev->datalen += datalen;
1394141cc406Sopenharmony_ci            dev->blocklen -= datalen;
1395141cc406Sopenharmony_ci            DBG(9, "<> got %lu, [%d, %d; %d]\n",
1396141cc406Sopenharmony_ci                (u_long)datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
1397141cc406Sopenharmony_ci            if (dev->blocklen < 0)
1398141cc406Sopenharmony_ci                return ret_cancel(dev, SANE_STATUS_IO_ERROR);
1399141cc406Sopenharmony_ci
1400141cc406Sopenharmony_ci            datalen = DATAROOM(dev) & USB_BLOCK_MASK;
1401141cc406Sopenharmony_ci        }
1402141cc406Sopenharmony_ci
1403141cc406Sopenharmony_ci        if (buf && lenp) { /* read mode */
1404141cc406Sopenharmony_ci            /* copy will do minimal of valid data */
1405141cc406Sopenharmony_ci            if (dev->para.format == SANE_FRAME_RGB && dev->line_order) {
1406141cc406Sopenharmony_ci                if (isJPEGEnabled(dev)) {
1407141cc406Sopenharmony_ci                    clrlen = dump_to_tmp_file(dev);
1408141cc406Sopenharmony_ci                    /* decompress after reading entire block data*/
1409141cc406Sopenharmony_ci                    if (0 == dev->blocklen) {
1410141cc406Sopenharmony_ci                        decompress_tempfile(dev);
1411141cc406Sopenharmony_ci                    }
1412141cc406Sopenharmony_ci                    copy_decompress_data(dev, buf, maxlen, &olen);
1413141cc406Sopenharmony_ci                } else {
1414141cc406Sopenharmony_ci                    clrlen = copy_mix_bands_trim(dev, buf, maxlen, &olen);
1415141cc406Sopenharmony_ci                }
1416141cc406Sopenharmony_ci            } else
1417141cc406Sopenharmony_ci                clrlen = copy_plain_trim(dev, buf, maxlen, &olen);
1418141cc406Sopenharmony_ci
1419141cc406Sopenharmony_ci            dev->datalen -= clrlen;
1420141cc406Sopenharmony_ci            dev->dataoff = (dev->dataoff + clrlen) & DATAMASK;
1421141cc406Sopenharmony_ci            buf += olen;
1422141cc406Sopenharmony_ci            maxlen -= olen;
1423141cc406Sopenharmony_ci            *lenp += olen;
1424141cc406Sopenharmony_ci            dev->total_out_size += olen;
1425141cc406Sopenharmony_ci
1426141cc406Sopenharmony_ci            DBG(9, "<> olen: %d, clrlen: %d, blocklen: %d/%d, maxlen %d (%d %d %d)\n",
1427141cc406Sopenharmony_ci                olen, clrlen, dev->blocklen, dev->datalen, maxlen,
1428141cc406Sopenharmony_ci                dev->dataindex / dev->bytes_per_line + dev->y_off,
1429141cc406Sopenharmony_ci                dev->y_off, dev->para.lines);
1430141cc406Sopenharmony_ci
1431141cc406Sopenharmony_ci            /* slack beyond last line */
1432141cc406Sopenharmony_ci            if (dev->dataindex / dev->bytes_per_line + dev->y_off >= dev->para.lines) {
1433141cc406Sopenharmony_ci                dev->datalen = 0;
1434141cc406Sopenharmony_ci                dev->dataoff = 0;
1435141cc406Sopenharmony_ci            }
1436141cc406Sopenharmony_ci
1437141cc406Sopenharmony_ci            if (!clrlen || maxlen <= 0)
1438141cc406Sopenharmony_ci                break;
1439141cc406Sopenharmony_ci        } else { /* flush mode */
1440141cc406Sopenharmony_ci            dev->datalen = 0;
1441141cc406Sopenharmony_ci            dev->dataoff = 0;
1442141cc406Sopenharmony_ci        }
1443141cc406Sopenharmony_ci
1444141cc406Sopenharmony_ci    } while (dev->blocklen);
1445141cc406Sopenharmony_ci
1446141cc406Sopenharmony_ci    if (lenp)
1447141cc406Sopenharmony_ci        DBG(9, " ==> %d\n", *lenp);
1448141cc406Sopenharmony_ci
1449141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1450141cc406Sopenharmony_ci}
1451141cc406Sopenharmony_ci
1452141cc406Sopenharmony_ciSANE_Status
1453141cc406Sopenharmony_cisane_start(SANE_Handle h)
1454141cc406Sopenharmony_ci{
1455141cc406Sopenharmony_ci    struct device *dev = h;
1456141cc406Sopenharmony_ci
1457141cc406Sopenharmony_ci    DBG(3, "%s: %p\n", __func__, h);
1458141cc406Sopenharmony_ci
1459141cc406Sopenharmony_ci    dev->cancel = 0;
1460141cc406Sopenharmony_ci    dev->scanning = 0;
1461141cc406Sopenharmony_ci    dev->total_img_size = 0;
1462141cc406Sopenharmony_ci    dev->total_out_size = 0;
1463141cc406Sopenharmony_ci    dev->total_data_size = 0;
1464141cc406Sopenharmony_ci    dev->blocks = 0;
1465141cc406Sopenharmony_ci
1466141cc406Sopenharmony_ci    if (!dev->reserved) {
1467141cc406Sopenharmony_ci        if (!dev_cmd_wait(dev, CMD_RESERVE_UNIT))
1468141cc406Sopenharmony_ci            return dev->state;
1469141cc406Sopenharmony_ci        dev->reserved++;
1470141cc406Sopenharmony_ci    }
1471141cc406Sopenharmony_ci
1472141cc406Sopenharmony_ci    if (!dev_set_window(dev) ||
1473141cc406Sopenharmony_ci        (dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
1474141cc406Sopenharmony_ci        return dev_stop(dev);
1475141cc406Sopenharmony_ci
1476141cc406Sopenharmony_ci    if (!dev_cmd_wait(dev, CMD_OBJECT_POSITION))
1477141cc406Sopenharmony_ci        return dev_stop(dev);
1478141cc406Sopenharmony_ci
1479141cc406Sopenharmony_ci    if (!dev_cmd(dev, CMD_READ) ||
1480141cc406Sopenharmony_ci        (dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
1481141cc406Sopenharmony_ci        return dev_stop(dev);
1482141cc406Sopenharmony_ci
1483141cc406Sopenharmony_ci    dev->scanning = 1;
1484141cc406Sopenharmony_ci    dev->final_block = 0;
1485141cc406Sopenharmony_ci    dev->blocklen = 0;
1486141cc406Sopenharmony_ci    dev->pixels_per_line = 0;
1487141cc406Sopenharmony_ci    dev->bytes_per_line = 0;
1488141cc406Sopenharmony_ci    dev->ulines = 0;
1489141cc406Sopenharmony_ci
1490141cc406Sopenharmony_ci    set_parameters(dev);
1491141cc406Sopenharmony_ci
1492141cc406Sopenharmony_ci    if (!dev->data && !(dev->data = malloc(DATASIZE)))
1493141cc406Sopenharmony_ci        return ret_cancel(dev, SANE_STATUS_NO_MEM);
1494141cc406Sopenharmony_ci
1495141cc406Sopenharmony_ci    /* this is for jpeg mode only */
1496141cc406Sopenharmony_ci    if (!dev->decData && !(dev->decData = malloc(POST_DATASIZE)))
1497141cc406Sopenharmony_ci        return ret_cancel(dev, SANE_STATUS_NO_MEM);
1498141cc406Sopenharmony_ci
1499141cc406Sopenharmony_ci    if (!dev_acquire(dev))
1500141cc406Sopenharmony_ci        return dev->state;
1501141cc406Sopenharmony_ci
1502141cc406Sopenharmony_ci    /* make sure to have dev->para <= of real size */
1503141cc406Sopenharmony_ci    if (dev->para.pixels_per_line > dev->pixels_per_line) {
1504141cc406Sopenharmony_ci        dev->para.pixels_per_line = dev->pixels_per_line;
1505141cc406Sopenharmony_ci        dev->para.bytes_per_line = dev->pixels_per_line;
1506141cc406Sopenharmony_ci    }
1507141cc406Sopenharmony_ci
1508141cc406Sopenharmony_ci    if (dev->composition == MODE_RGB24)
1509141cc406Sopenharmony_ci        dev->para.bytes_per_line = dev->para.pixels_per_line * 3;
1510141cc406Sopenharmony_ci    else if (dev->composition == MODE_LINEART ||
1511141cc406Sopenharmony_ci             dev->composition == MODE_HALFTONE) {
1512141cc406Sopenharmony_ci        dev->para.bytes_per_line = (dev->para.pixels_per_line + 7) / 8;
1513141cc406Sopenharmony_ci        dev->para.pixels_per_line = dev->para.bytes_per_line * 8;
1514141cc406Sopenharmony_ci    } else {
1515141cc406Sopenharmony_ci        dev->para.bytes_per_line = dev->para.pixels_per_line;
1516141cc406Sopenharmony_ci    }
1517141cc406Sopenharmony_ci
1518141cc406Sopenharmony_ci    dev->total_img_size = dev->para.bytes_per_line * dev->para.lines;
1519141cc406Sopenharmony_ci
1520141cc406Sopenharmony_ci    if (isJPEGEnabled(dev) &&
1521141cc406Sopenharmony_ci        dev->composition == MODE_RGB24) {
1522141cc406Sopenharmony_ci	int fd;
1523141cc406Sopenharmony_ci        remove(encTmpFileName);
1524141cc406Sopenharmony_ci
1525141cc406Sopenharmony_ci	/* Precreate temporary file in exclusive mode. */
1526141cc406Sopenharmony_ci	fd = open(encTmpFileName, O_CREAT|O_EXCL, 0600);
1527141cc406Sopenharmony_ci	if (fd == -1) {
1528141cc406Sopenharmony_ci	    DBG(3, "%s: %p, can't create temporary file %s: %s\n", __func__,
1529141cc406Sopenharmony_ci		(void *)dev, encTmpFileName, strerror(errno));
1530141cc406Sopenharmony_ci	    return ret_cancel(dev, SANE_STATUS_ACCESS_DENIED);
1531141cc406Sopenharmony_ci	}
1532141cc406Sopenharmony_ci	close(fd);
1533141cc406Sopenharmony_ci    }
1534141cc406Sopenharmony_ci    dev->currentDecDataIndex = 0;
1535141cc406Sopenharmony_ci
1536141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1537141cc406Sopenharmony_ci}
1538141cc406Sopenharmony_ci
1539141cc406Sopenharmony_ciSANE_Status sane_set_io_mode(SANE_Handle h, SANE_Bool non_blocking)
1540141cc406Sopenharmony_ci{
1541141cc406Sopenharmony_ci    struct device *dev = h;
1542141cc406Sopenharmony_ci
1543141cc406Sopenharmony_ci    DBG(3, "%s: %p, %d\n", __func__, h, non_blocking);
1544141cc406Sopenharmony_ci
1545141cc406Sopenharmony_ci    if (non_blocking)
1546141cc406Sopenharmony_ci        return SANE_STATUS_UNSUPPORTED;
1547141cc406Sopenharmony_ci
1548141cc406Sopenharmony_ci    dev->non_blocking = non_blocking;
1549141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1550141cc406Sopenharmony_ci}
1551141cc406Sopenharmony_ci
1552141cc406Sopenharmony_ciSANE_Status sane_get_select_fd(SANE_Handle h, SANE_Int *fdp)
1553141cc406Sopenharmony_ci{
1554141cc406Sopenharmony_ci    DBG(3, "%s: %p, %p\n", __func__, h, (void *)fdp);
1555141cc406Sopenharmony_ci    /* supporting of this will require thread creation */
1556141cc406Sopenharmony_ci    return SANE_STATUS_UNSUPPORTED;
1557141cc406Sopenharmony_ci}
1558141cc406Sopenharmony_ci
1559141cc406Sopenharmony_civoid sane_cancel(SANE_Handle h)
1560141cc406Sopenharmony_ci{
1561141cc406Sopenharmony_ci    struct device *dev = h;
1562141cc406Sopenharmony_ci
1563141cc406Sopenharmony_ci    DBG(3, "%s: %p\n", __func__, h);
1564141cc406Sopenharmony_ci    dev->cancel = 1;
1565141cc406Sopenharmony_ci}
1566141cc406Sopenharmony_ci
1567141cc406Sopenharmony_ci/* xerox_mfp.c */
1568