1141cc406Sopenharmony_ci/*
2141cc406Sopenharmony_ci * SANE - Scanner Access Now Easy.
3141cc406Sopenharmony_ci * coolscan3.c
4141cc406Sopenharmony_ci *
5141cc406Sopenharmony_ci * This file implements a SANE backend for Nikon Coolscan film scanners.
6141cc406Sopenharmony_ci *
7141cc406Sopenharmony_ci * coolscan3.c is based on coolscan2.c, a work of András Major, Ariel Garcia
8141cc406Sopenharmony_ci * and Giuseppe Sacco.
9141cc406Sopenharmony_ci *
10141cc406Sopenharmony_ci * Copyright (C) 2007-08 Tower Technologies
11141cc406Sopenharmony_ci * Author: Alessandro Zummo <a.zummo@towertech.it>
12141cc406Sopenharmony_ci *
13141cc406Sopenharmony_ci * This file is part of the SANE package.
14141cc406Sopenharmony_ci *
15141cc406Sopenharmony_ci * This program is free software; you can redistribute it and/or
16141cc406Sopenharmony_ci * modify it under the terms of the GNU General Public License as
17141cc406Sopenharmony_ci * published by the Free Software Foundation, version 2.
18141cc406Sopenharmony_ci *
19141cc406Sopenharmony_ci */
20141cc406Sopenharmony_ci
21141cc406Sopenharmony_ci/* ========================================================================= */
22141cc406Sopenharmony_ci
23141cc406Sopenharmony_ci#include "../include/sane/config.h"
24141cc406Sopenharmony_ci
25141cc406Sopenharmony_ci#include <math.h>
26141cc406Sopenharmony_ci#include <stdio.h>
27141cc406Sopenharmony_ci#include <stdlib.h>
28141cc406Sopenharmony_ci#include <string.h>
29141cc406Sopenharmony_ci#include <ctype.h>
30141cc406Sopenharmony_ci#include <unistd.h>
31141cc406Sopenharmony_ci#include <time.h>
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_ci#include "../include/_stdint.h"
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci#include "../include/sane/sane.h"
36141cc406Sopenharmony_ci#include "../include/sane/sanei.h"
37141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
38141cc406Sopenharmony_ci#include "../include/sane/sanei_scsi.h"
39141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
40141cc406Sopenharmony_ci#include "../include/sane/sanei_debug.h"
41141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci#define BACKEND_NAME coolscan3
44141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"	/* must be last */
45141cc406Sopenharmony_ci
46141cc406Sopenharmony_ci#define CS3_VERSION_MAJOR 1
47141cc406Sopenharmony_ci#define CS3_VERSION_MINOR 0
48141cc406Sopenharmony_ci#define CS3_REVISION 0
49141cc406Sopenharmony_ci#define CS3_CONFIG_FILE "coolscan3.conf"
50141cc406Sopenharmony_ci
51141cc406Sopenharmony_ci#define WSIZE (sizeof (SANE_Word))
52141cc406Sopenharmony_ci
53141cc406Sopenharmony_ci
54141cc406Sopenharmony_ci/* ========================================================================= */
55141cc406Sopenharmony_ci/* typedefs */
56141cc406Sopenharmony_ci
57141cc406Sopenharmony_citypedef enum
58141cc406Sopenharmony_ci{
59141cc406Sopenharmony_ci	CS3_TYPE_UNKOWN,
60141cc406Sopenharmony_ci	CS3_TYPE_LS30,
61141cc406Sopenharmony_ci	CS3_TYPE_LS40,
62141cc406Sopenharmony_ci	CS3_TYPE_LS50,
63141cc406Sopenharmony_ci	CS3_TYPE_LS2000,
64141cc406Sopenharmony_ci	CS3_TYPE_LS4000,
65141cc406Sopenharmony_ci	CS3_TYPE_LS5000,
66141cc406Sopenharmony_ci	CS3_TYPE_LS8000
67141cc406Sopenharmony_ci}
68141cc406Sopenharmony_cics3_type_t;
69141cc406Sopenharmony_ci
70141cc406Sopenharmony_citypedef enum
71141cc406Sopenharmony_ci{
72141cc406Sopenharmony_ci	CS3_INTERFACE_UNKNOWN,
73141cc406Sopenharmony_ci	CS3_INTERFACE_SCSI,	/* includes IEEE1394 via SBP2 */
74141cc406Sopenharmony_ci	CS3_INTERFACE_USB
75141cc406Sopenharmony_ci}
76141cc406Sopenharmony_cics3_interface_t;
77141cc406Sopenharmony_ci
78141cc406Sopenharmony_citypedef enum
79141cc406Sopenharmony_ci{
80141cc406Sopenharmony_ci	CS3_PHASE_NONE = 0x00,
81141cc406Sopenharmony_ci	CS3_PHASE_STATUS = 0x01,
82141cc406Sopenharmony_ci	CS3_PHASE_OUT = 0x02,
83141cc406Sopenharmony_ci	CS3_PHASE_IN = 0x03,
84141cc406Sopenharmony_ci	CS3_PHASE_BUSY = 0x04
85141cc406Sopenharmony_ci}
86141cc406Sopenharmony_cics3_phase_t;
87141cc406Sopenharmony_ci
88141cc406Sopenharmony_citypedef enum
89141cc406Sopenharmony_ci{
90141cc406Sopenharmony_ci	CS3_SCAN_NORMAL,
91141cc406Sopenharmony_ci	CS3_SCAN_AE,
92141cc406Sopenharmony_ci	CS3_SCAN_AE_WB
93141cc406Sopenharmony_ci}
94141cc406Sopenharmony_cics3_scan_t;
95141cc406Sopenharmony_ci
96141cc406Sopenharmony_citypedef enum
97141cc406Sopenharmony_ci{
98141cc406Sopenharmony_ci	CS3_STATUS_READY = 0,
99141cc406Sopenharmony_ci	CS3_STATUS_BUSY = 1,
100141cc406Sopenharmony_ci	CS3_STATUS_NO_DOCS = 2,
101141cc406Sopenharmony_ci	CS3_STATUS_PROCESSING = 4,
102141cc406Sopenharmony_ci	CS3_STATUS_ERROR = 8,
103141cc406Sopenharmony_ci	CS3_STATUS_REISSUE = 16,
104141cc406Sopenharmony_ci	CS3_STATUS_ALL = 31	/* sum of all others */
105141cc406Sopenharmony_ci}
106141cc406Sopenharmony_cics3_status_t;
107141cc406Sopenharmony_ci
108141cc406Sopenharmony_citypedef enum
109141cc406Sopenharmony_ci{
110141cc406Sopenharmony_ci	CS3_OPTION_NUM = 0,
111141cc406Sopenharmony_ci
112141cc406Sopenharmony_ci	CS3_OPTION_PREVIEW,
113141cc406Sopenharmony_ci
114141cc406Sopenharmony_ci	CS3_OPTION_NEGATIVE,
115141cc406Sopenharmony_ci
116141cc406Sopenharmony_ci	CS3_OPTION_INFRARED,
117141cc406Sopenharmony_ci
118141cc406Sopenharmony_ci	CS3_OPTION_SAMPLES_PER_SCAN,
119141cc406Sopenharmony_ci
120141cc406Sopenharmony_ci	CS3_OPTION_DEPTH,
121141cc406Sopenharmony_ci
122141cc406Sopenharmony_ci	CS3_OPTION_EXPOSURE,
123141cc406Sopenharmony_ci	CS3_OPTION_EXPOSURE_R,
124141cc406Sopenharmony_ci	CS3_OPTION_EXPOSURE_G,
125141cc406Sopenharmony_ci	CS3_OPTION_EXPOSURE_B,
126141cc406Sopenharmony_ci	CS3_OPTION_SCAN_AE,
127141cc406Sopenharmony_ci	CS3_OPTION_SCAN_AE_WB,
128141cc406Sopenharmony_ci
129141cc406Sopenharmony_ci	CS3_OPTION_LUT_R,
130141cc406Sopenharmony_ci	CS3_OPTION_LUT_G,
131141cc406Sopenharmony_ci	CS3_OPTION_LUT_B,
132141cc406Sopenharmony_ci
133141cc406Sopenharmony_ci	CS3_OPTION_RES,
134141cc406Sopenharmony_ci	CS3_OPTION_RESX,
135141cc406Sopenharmony_ci	CS3_OPTION_RESY,
136141cc406Sopenharmony_ci	CS3_OPTION_RES_INDEPENDENT,
137141cc406Sopenharmony_ci
138141cc406Sopenharmony_ci	CS3_OPTION_PREVIEW_RESOLUTION,
139141cc406Sopenharmony_ci
140141cc406Sopenharmony_ci	CS3_OPTION_FRAME,
141141cc406Sopenharmony_ci	CS3_OPTION_FRAME_COUNT,
142141cc406Sopenharmony_ci	CS3_OPTION_SUBFRAME,
143141cc406Sopenharmony_ci	CS3_OPTION_XMIN,
144141cc406Sopenharmony_ci	CS3_OPTION_XMAX,
145141cc406Sopenharmony_ci	CS3_OPTION_YMIN,
146141cc406Sopenharmony_ci	CS3_OPTION_YMAX,
147141cc406Sopenharmony_ci
148141cc406Sopenharmony_ci	CS3_OPTION_LOAD,
149141cc406Sopenharmony_ci	CS3_OPTION_AUTOLOAD,
150141cc406Sopenharmony_ci	CS3_OPTION_EJECT,
151141cc406Sopenharmony_ci	CS3_OPTION_RESET,
152141cc406Sopenharmony_ci
153141cc406Sopenharmony_ci	CS3_OPTION_FOCUS_ON_CENTRE,
154141cc406Sopenharmony_ci	CS3_OPTION_FOCUS,
155141cc406Sopenharmony_ci	CS3_OPTION_AUTOFOCUS,
156141cc406Sopenharmony_ci	CS3_OPTION_FOCUSX,
157141cc406Sopenharmony_ci	CS3_OPTION_FOCUSY,
158141cc406Sopenharmony_ci
159141cc406Sopenharmony_ci	CS3_N_OPTIONS		/* must be last -- counts number of enum items */
160141cc406Sopenharmony_ci}
161141cc406Sopenharmony_cics3_option_t;
162141cc406Sopenharmony_ci
163141cc406Sopenharmony_citypedef unsigned int cs3_pixel_t;
164141cc406Sopenharmony_ci
165141cc406Sopenharmony_ci#define CS3_COLOR_MAX 10	/* 9 + 1, see cs3_colors */
166141cc406Sopenharmony_ci
167141cc406Sopenharmony_ci/* Given that there is no way to give scanner vendor
168141cc406Sopenharmony_ci * and model to the calling software, I have to use
169141cc406Sopenharmony_ci * an ugly hack here. :( That's very sad. Suggestions
170141cc406Sopenharmony_ci * that can provide the same features are appreciated.
171141cc406Sopenharmony_ci */
172141cc406Sopenharmony_ci
173141cc406Sopenharmony_ci#ifndef SANE_COOKIE
174141cc406Sopenharmony_ci#define SANE_COOKIE 0x0BADCAFE
175141cc406Sopenharmony_ci
176141cc406Sopenharmony_cistruct SANE_Cookie
177141cc406Sopenharmony_ci{
178141cc406Sopenharmony_ci	uint16_t version;
179141cc406Sopenharmony_ci	const char *vendor;
180141cc406Sopenharmony_ci	const char *model;
181141cc406Sopenharmony_ci	const char *revision;
182141cc406Sopenharmony_ci};
183141cc406Sopenharmony_ci#endif
184141cc406Sopenharmony_ci
185141cc406Sopenharmony_citypedef struct
186141cc406Sopenharmony_ci{
187141cc406Sopenharmony_ci	/* magic bits :( */
188141cc406Sopenharmony_ci	uint32_t magic;
189141cc406Sopenharmony_ci	struct SANE_Cookie *cookie_ptr;
190141cc406Sopenharmony_ci	struct SANE_Cookie cookie;
191141cc406Sopenharmony_ci
192141cc406Sopenharmony_ci	/* interface */
193141cc406Sopenharmony_ci	cs3_interface_t interface;
194141cc406Sopenharmony_ci	int fd;
195141cc406Sopenharmony_ci	SANE_Byte *send_buf, *recv_buf;
196141cc406Sopenharmony_ci	size_t send_buf_size, recv_buf_size;
197141cc406Sopenharmony_ci	size_t n_cmd, n_send, n_recv;
198141cc406Sopenharmony_ci
199141cc406Sopenharmony_ci	/* device characteristics */
200141cc406Sopenharmony_ci	char vendor_string[9], product_string[17], revision_string[5];
201141cc406Sopenharmony_ci	cs3_type_t type;
202141cc406Sopenharmony_ci	int maxbits;
203141cc406Sopenharmony_ci	unsigned int resx_optical, resx_min, resx_max, *resx_list,
204141cc406Sopenharmony_ci		resx_n_list;
205141cc406Sopenharmony_ci	unsigned int resy_optical, resy_min, resy_max, *resy_list,
206141cc406Sopenharmony_ci		resy_n_list;
207141cc406Sopenharmony_ci	unsigned long boundaryx, boundaryy;
208141cc406Sopenharmony_ci	unsigned long frame_offset;
209141cc406Sopenharmony_ci	unsigned int unit_dpi;
210141cc406Sopenharmony_ci	double unit_mm;
211141cc406Sopenharmony_ci	int n_frames;
212141cc406Sopenharmony_ci
213141cc406Sopenharmony_ci	int focus_min, focus_max;
214141cc406Sopenharmony_ci
215141cc406Sopenharmony_ci	/* settings */
216141cc406Sopenharmony_ci	SANE_Bool preview, negative, infrared, autoload, autofocus, ae, aewb;
217141cc406Sopenharmony_ci	int samples_per_scan, depth, real_depth, bytes_per_pixel, shift_bits,
218141cc406Sopenharmony_ci		n_colors;
219141cc406Sopenharmony_ci	cs3_pixel_t n_lut;
220141cc406Sopenharmony_ci	cs3_pixel_t *lut_r, *lut_g, *lut_b, *lut_neutral;
221141cc406Sopenharmony_ci	unsigned long resx, resy, res, res_independent, res_preview;
222141cc406Sopenharmony_ci	unsigned long xmin, xmax, ymin, ymax;
223141cc406Sopenharmony_ci	int i_frame, frame_count;
224141cc406Sopenharmony_ci	double subframe;
225141cc406Sopenharmony_ci
226141cc406Sopenharmony_ci	unsigned int real_resx, real_resy, real_pitchx, real_pitchy;
227141cc406Sopenharmony_ci	unsigned long real_xoffset, real_yoffset, real_width, real_height,
228141cc406Sopenharmony_ci		logical_width, logical_height;
229141cc406Sopenharmony_ci	int odd_padding;
230141cc406Sopenharmony_ci	int block_padding;
231141cc406Sopenharmony_ci
232141cc406Sopenharmony_ci	double exposure, exposure_r, exposure_g, exposure_b;
233141cc406Sopenharmony_ci	unsigned long real_exposure[CS3_COLOR_MAX];
234141cc406Sopenharmony_ci
235141cc406Sopenharmony_ci
236141cc406Sopenharmony_ci	SANE_Bool focus_on_centre;
237141cc406Sopenharmony_ci	unsigned long focusx, focusy, real_focusx, real_focusy;
238141cc406Sopenharmony_ci	int focus;
239141cc406Sopenharmony_ci
240141cc406Sopenharmony_ci	/* status */
241141cc406Sopenharmony_ci	SANE_Bool scanning;
242141cc406Sopenharmony_ci	SANE_Byte *line_buf;
243141cc406Sopenharmony_ci	ssize_t n_line_buf, i_line_buf;
244141cc406Sopenharmony_ci	unsigned long sense_key, sense_asc, sense_ascq, sense_info;
245141cc406Sopenharmony_ci	unsigned long sense_code;
246141cc406Sopenharmony_ci	cs3_status_t status;
247141cc406Sopenharmony_ci	size_t xfer_position, xfer_bytes_total;
248141cc406Sopenharmony_ci
249141cc406Sopenharmony_ci	/* SANE stuff */
250141cc406Sopenharmony_ci	SANE_Option_Descriptor option_list[CS3_N_OPTIONS];
251141cc406Sopenharmony_ci}
252141cc406Sopenharmony_cics3_t;
253141cc406Sopenharmony_ci
254141cc406Sopenharmony_ci
255141cc406Sopenharmony_ci/* ========================================================================= */
256141cc406Sopenharmony_ci/* prototypes */
257141cc406Sopenharmony_ci
258141cc406Sopenharmony_cistatic SANE_Status cs3_open(const char *device, cs3_interface_t interface,
259141cc406Sopenharmony_ci			    cs3_t ** sp);
260141cc406Sopenharmony_cistatic void cs3_close(cs3_t * s);
261141cc406Sopenharmony_cistatic SANE_Status cs3_attach(const char *dev);
262141cc406Sopenharmony_cistatic SANE_Status cs3_scsi_sense_handler(int fd, u_char * sense_buffer,
263141cc406Sopenharmony_ci					  void *arg);
264141cc406Sopenharmony_cistatic SANE_Status cs3_parse_sense_data(cs3_t * s);
265141cc406Sopenharmony_cistatic void cs3_init_buffer(cs3_t * s);
266141cc406Sopenharmony_cistatic SANE_Status cs3_pack_byte(cs3_t * s, SANE_Byte byte);
267141cc406Sopenharmony_cistatic void cs3_pack_long(cs3_t * s, unsigned long val);
268141cc406Sopenharmony_cistatic void cs3_pack_word(cs3_t * s, unsigned long val);
269141cc406Sopenharmony_cistatic SANE_Status cs3_parse_cmd(cs3_t * s, char *text);
270141cc406Sopenharmony_cistatic SANE_Status cs3_grow_send_buffer(cs3_t * s);
271141cc406Sopenharmony_cistatic SANE_Status cs3_issue_cmd(cs3_t * s);
272141cc406Sopenharmony_cistatic cs3_phase_t cs3_phase_check(cs3_t * s);
273141cc406Sopenharmony_cistatic SANE_Status cs3_set_boundary(cs3_t * s);
274141cc406Sopenharmony_cistatic SANE_Status cs3_scanner_ready(cs3_t * s, int flags);
275141cc406Sopenharmony_cistatic SANE_Status cs3_page_inquiry(cs3_t * s, int page);
276141cc406Sopenharmony_cistatic SANE_Status cs3_full_inquiry(cs3_t * s);
277141cc406Sopenharmony_cistatic SANE_Status cs3_mode_select(cs3_t * s);
278141cc406Sopenharmony_cistatic SANE_Status cs3_reserve_unit(cs3_t * s);
279141cc406Sopenharmony_cistatic SANE_Status cs3_release_unit(cs3_t * s);
280141cc406Sopenharmony_cistatic SANE_Status cs3_execute(cs3_t * s);
281141cc406Sopenharmony_cistatic SANE_Status cs3_load(cs3_t * s);
282141cc406Sopenharmony_cistatic SANE_Status cs3_eject(cs3_t * s);
283141cc406Sopenharmony_cistatic SANE_Status cs3_reset(cs3_t * s);
284141cc406Sopenharmony_cistatic SANE_Status cs3_set_focus(cs3_t * s);
285141cc406Sopenharmony_cistatic SANE_Status cs3_autofocus(cs3_t * s);
286141cc406Sopenharmony_cistatic SANE_Status cs3_autoexposure(cs3_t * s, int wb);
287141cc406Sopenharmony_cistatic SANE_Status cs3_get_exposure(cs3_t * s);
288141cc406Sopenharmony_cistatic SANE_Status cs3_set_window(cs3_t * s, cs3_scan_t type);
289141cc406Sopenharmony_cistatic SANE_Status cs3_convert_options(cs3_t * s);
290141cc406Sopenharmony_cistatic SANE_Status cs3_scan(cs3_t * s, cs3_scan_t type);
291141cc406Sopenharmony_cistatic void *cs3_xmalloc(size_t size);
292141cc406Sopenharmony_cistatic void *cs3_xrealloc(void *p, size_t size);
293141cc406Sopenharmony_cistatic void cs3_xfree(void *p);
294141cc406Sopenharmony_ci
295141cc406Sopenharmony_ci
296141cc406Sopenharmony_ci/* ========================================================================= */
297141cc406Sopenharmony_ci/* global variables */
298141cc406Sopenharmony_ci
299141cc406Sopenharmony_cistatic int cs3_colors[] = { 1, 2, 3, 9 };
300141cc406Sopenharmony_ci
301141cc406Sopenharmony_cistatic SANE_Device **device_list = NULL;
302141cc406Sopenharmony_cistatic int n_device_list = 0;
303141cc406Sopenharmony_cistatic cs3_interface_t try_interface = CS3_INTERFACE_UNKNOWN;
304141cc406Sopenharmony_cistatic int open_devices = 0;
305141cc406Sopenharmony_ci
306141cc406Sopenharmony_ci
307141cc406Sopenharmony_ci/* ========================================================================= */
308141cc406Sopenharmony_ci/* SANE entry points */
309141cc406Sopenharmony_ci
310141cc406Sopenharmony_ciSANE_Status
311141cc406Sopenharmony_cisane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
312141cc406Sopenharmony_ci{
313141cc406Sopenharmony_ci	DBG_INIT();
314141cc406Sopenharmony_ci	DBG(1, "coolscan3 backend, version %i.%i.%i initializing.\n",
315141cc406Sopenharmony_ci	    CS3_VERSION_MAJOR, CS3_VERSION_MINOR, CS3_REVISION);
316141cc406Sopenharmony_ci
317141cc406Sopenharmony_ci	(void) authorize;	/* to shut up compiler */
318141cc406Sopenharmony_ci
319141cc406Sopenharmony_ci	if (version_code)
320141cc406Sopenharmony_ci		*version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
321141cc406Sopenharmony_ci
322141cc406Sopenharmony_ci	sanei_usb_init();
323141cc406Sopenharmony_ci
324141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
325141cc406Sopenharmony_ci}
326141cc406Sopenharmony_ci
327141cc406Sopenharmony_civoid
328141cc406Sopenharmony_cisane_exit(void)
329141cc406Sopenharmony_ci{
330141cc406Sopenharmony_ci	int i;
331141cc406Sopenharmony_ci
332141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
333141cc406Sopenharmony_ci
334141cc406Sopenharmony_ci	for (i = 0; i < n_device_list; i++) {
335141cc406Sopenharmony_ci		cs3_xfree((void *)device_list[i]->name);
336141cc406Sopenharmony_ci		cs3_xfree((void *)device_list[i]->vendor);
337141cc406Sopenharmony_ci		cs3_xfree((void *)device_list[i]->model);
338141cc406Sopenharmony_ci		cs3_xfree(device_list[i]);
339141cc406Sopenharmony_ci	}
340141cc406Sopenharmony_ci	cs3_xfree(device_list);
341141cc406Sopenharmony_ci}
342141cc406Sopenharmony_ci
343141cc406Sopenharmony_ciSANE_Status
344141cc406Sopenharmony_cisane_get_devices(const SANE_Device *** list, SANE_Bool local_only)
345141cc406Sopenharmony_ci{
346141cc406Sopenharmony_ci	char line[PATH_MAX], *p;
347141cc406Sopenharmony_ci	FILE *config;
348141cc406Sopenharmony_ci
349141cc406Sopenharmony_ci	(void) local_only;	/* to shut up compiler */
350141cc406Sopenharmony_ci
351141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
352141cc406Sopenharmony_ci
353141cc406Sopenharmony_ci	if (device_list)
354141cc406Sopenharmony_ci		DBG(6,
355141cc406Sopenharmony_ci		    "sane_get_devices(): Device list already populated, not probing again.\n");
356141cc406Sopenharmony_ci	else {
357141cc406Sopenharmony_ci		if (open_devices) {
358141cc406Sopenharmony_ci			DBG(4,
359141cc406Sopenharmony_ci			    "sane_get_devices(): Devices open, not scanning for scanners.\n");
360141cc406Sopenharmony_ci			return SANE_STATUS_IO_ERROR;
361141cc406Sopenharmony_ci		}
362141cc406Sopenharmony_ci
363141cc406Sopenharmony_ci		config = sanei_config_open(CS3_CONFIG_FILE);
364141cc406Sopenharmony_ci		if (config) {
365141cc406Sopenharmony_ci			DBG(4, "sane_get_devices(): Reading config file.\n");
366141cc406Sopenharmony_ci			while (sanei_config_read(line, sizeof(line), config)) {
367141cc406Sopenharmony_ci				p = line;
368141cc406Sopenharmony_ci				p += strspn(line, " \t");
369141cc406Sopenharmony_ci				if (strlen(p) && (p[0] != '\n')
370141cc406Sopenharmony_ci				    && (p[0] != '#'))
371141cc406Sopenharmony_ci					cs3_open(line, CS3_INTERFACE_UNKNOWN,
372141cc406Sopenharmony_ci						 NULL);
373141cc406Sopenharmony_ci			}
374141cc406Sopenharmony_ci			fclose(config);
375141cc406Sopenharmony_ci		} else {
376141cc406Sopenharmony_ci			DBG(4, "sane_get_devices(): No config file found.\n");
377141cc406Sopenharmony_ci			cs3_open("auto", CS3_INTERFACE_UNKNOWN, NULL);
378141cc406Sopenharmony_ci		}
379141cc406Sopenharmony_ci
380141cc406Sopenharmony_ci		DBG(6, "%s: %i device(s) detected.\n",
381141cc406Sopenharmony_ci		    __func__, n_device_list);
382141cc406Sopenharmony_ci	}
383141cc406Sopenharmony_ci
384141cc406Sopenharmony_ci	*list = (const SANE_Device **) device_list;
385141cc406Sopenharmony_ci
386141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
387141cc406Sopenharmony_ci}
388141cc406Sopenharmony_ci
389141cc406Sopenharmony_ciSANE_Status
390141cc406Sopenharmony_cisane_open(SANE_String_Const name, SANE_Handle * h)
391141cc406Sopenharmony_ci{
392141cc406Sopenharmony_ci	SANE_Status status;
393141cc406Sopenharmony_ci	cs3_t *s;
394141cc406Sopenharmony_ci	int i_option;
395141cc406Sopenharmony_ci	unsigned int i_list;
396141cc406Sopenharmony_ci	SANE_Option_Descriptor o;
397141cc406Sopenharmony_ci	SANE_Word *word_list;
398141cc406Sopenharmony_ci	SANE_Range *range = NULL;
399141cc406Sopenharmony_ci	int alloc_failed = 0;
400141cc406Sopenharmony_ci
401141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
402141cc406Sopenharmony_ci
403141cc406Sopenharmony_ci	status = cs3_open(name, CS3_INTERFACE_UNKNOWN, &s);
404141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
405141cc406Sopenharmony_ci		return status;
406141cc406Sopenharmony_ci
407141cc406Sopenharmony_ci	*h = (SANE_Handle) s;
408141cc406Sopenharmony_ci
409141cc406Sopenharmony_ci	/* get device properties */
410141cc406Sopenharmony_ci
411141cc406Sopenharmony_ci	s->lut_r = s->lut_g = s->lut_b = s->lut_neutral = NULL;
412141cc406Sopenharmony_ci	s->resx_list = s->resy_list = NULL;
413141cc406Sopenharmony_ci	s->resx_n_list = s->resy_n_list = 0;
414141cc406Sopenharmony_ci
415141cc406Sopenharmony_ci	status = cs3_full_inquiry(s);
416141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
417141cc406Sopenharmony_ci		return status;
418141cc406Sopenharmony_ci
419141cc406Sopenharmony_ci	status = cs3_mode_select(s);
420141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
421141cc406Sopenharmony_ci		return status;
422141cc406Sopenharmony_ci
423141cc406Sopenharmony_ci	/* option descriptors */
424141cc406Sopenharmony_ci
425141cc406Sopenharmony_ci	for (i_option = 0; i_option < CS3_N_OPTIONS; i_option++) {
426141cc406Sopenharmony_ci		o.name = o.title = o.desc = NULL;
427141cc406Sopenharmony_ci		o.type = SANE_TYPE_BOOL;
428141cc406Sopenharmony_ci		o.unit = SANE_UNIT_NONE;
429141cc406Sopenharmony_ci		o.size = o.cap = 0;
430141cc406Sopenharmony_ci		o.constraint_type = SANE_CONSTRAINT_NONE;
431141cc406Sopenharmony_ci		o.constraint.range = NULL;	/* only one union member needs to be NULLed */
432141cc406Sopenharmony_ci		switch (i_option) {
433141cc406Sopenharmony_ci		case CS3_OPTION_NUM:
434141cc406Sopenharmony_ci			o.name = "";
435141cc406Sopenharmony_ci			o.title = SANE_TITLE_NUM_OPTIONS;
436141cc406Sopenharmony_ci			o.desc = SANE_DESC_NUM_OPTIONS;
437141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
438141cc406Sopenharmony_ci			o.size = WSIZE;
439141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_DETECT;
440141cc406Sopenharmony_ci			break;
441141cc406Sopenharmony_ci		case CS3_OPTION_PREVIEW:
442141cc406Sopenharmony_ci			o.name = "preview";
443141cc406Sopenharmony_ci			o.title = "Preview mode";
444141cc406Sopenharmony_ci			o.desc = "Preview mode";
445141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
446141cc406Sopenharmony_ci			o.size = WSIZE;
447141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
448141cc406Sopenharmony_ci				SANE_CAP_ADVANCED;
449141cc406Sopenharmony_ci			break;
450141cc406Sopenharmony_ci		case CS3_OPTION_NEGATIVE:
451141cc406Sopenharmony_ci			o.name = "negative";
452141cc406Sopenharmony_ci			o.title = "Negative";
453141cc406Sopenharmony_ci			o.desc = "Negative film: make scanner invert colors";
454141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
455141cc406Sopenharmony_ci			o.size = WSIZE;
456141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
457141cc406Sopenharmony_ci			/*o.cap |= SANE_CAP_INACTIVE; */
458141cc406Sopenharmony_ci			break;
459141cc406Sopenharmony_ci
460141cc406Sopenharmony_ci		case CS3_OPTION_INFRARED:
461141cc406Sopenharmony_ci			o.name = "infrared";
462141cc406Sopenharmony_ci			o.title = "Read infrared channel";
463141cc406Sopenharmony_ci			o.desc = "Read infrared channel in addition to scan colors";
464141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
465141cc406Sopenharmony_ci			o.size = WSIZE;
466141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
467141cc406Sopenharmony_ci#ifndef SANE_FRAME_RGBI
468141cc406Sopenharmony_ci                        o.cap |= SANE_CAP_INACTIVE;
469141cc406Sopenharmony_ci#endif
470141cc406Sopenharmony_ci			break;
471141cc406Sopenharmony_ci
472141cc406Sopenharmony_ci		case CS3_OPTION_SAMPLES_PER_SCAN:
473141cc406Sopenharmony_ci			o.name = "samples-per-scan";
474141cc406Sopenharmony_ci			o.title = "Samples per Scan";
475141cc406Sopenharmony_ci			o.desc = "Number of samples per scan";
476141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
477141cc406Sopenharmony_ci			o.unit = SANE_UNIT_NONE;
478141cc406Sopenharmony_ci			o.size = WSIZE;
479141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
480141cc406Sopenharmony_ci			if (s->type != CS3_TYPE_LS2000 && s->type != CS3_TYPE_LS4000
481141cc406Sopenharmony_ci					&& s->type != CS3_TYPE_LS5000 && s->type != CS3_TYPE_LS8000)
482141cc406Sopenharmony_ci				o.cap |= SANE_CAP_INACTIVE;
483141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
484141cc406Sopenharmony_ci			range = (SANE_Range *) cs3_xmalloc (sizeof (SANE_Range));
485141cc406Sopenharmony_ci			if (! range)
486141cc406Sopenharmony_ci				  alloc_failed = 1;
487141cc406Sopenharmony_ci			else
488141cc406Sopenharmony_ci				  {
489141cc406Sopenharmony_ci					range->min = 1;
490141cc406Sopenharmony_ci					range->max = 16;
491141cc406Sopenharmony_ci					range->quant = 1;
492141cc406Sopenharmony_ci					o.constraint.range = range;
493141cc406Sopenharmony_ci				  }
494141cc406Sopenharmony_ci			break;
495141cc406Sopenharmony_ci
496141cc406Sopenharmony_ci		case CS3_OPTION_DEPTH:
497141cc406Sopenharmony_ci			o.name = "depth";
498141cc406Sopenharmony_ci			o.title = "Bit depth per channel";
499141cc406Sopenharmony_ci			o.desc = "Number of bits output by scanner for each channel";
500141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
501141cc406Sopenharmony_ci			o.unit = SANE_UNIT_NONE;
502141cc406Sopenharmony_ci			o.size = WSIZE;
503141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
504141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
505141cc406Sopenharmony_ci			word_list =
506141cc406Sopenharmony_ci				(SANE_Word *) cs3_xmalloc(2 *
507141cc406Sopenharmony_ci							  sizeof(SANE_Word));
508141cc406Sopenharmony_ci			if (!word_list)
509141cc406Sopenharmony_ci				alloc_failed = 1;
510141cc406Sopenharmony_ci			else {
511141cc406Sopenharmony_ci				word_list[1] = 8;
512141cc406Sopenharmony_ci				word_list[2] = s->maxbits;
513141cc406Sopenharmony_ci				word_list[0] = 2;
514141cc406Sopenharmony_ci				o.constraint.word_list = word_list;
515141cc406Sopenharmony_ci			}
516141cc406Sopenharmony_ci			break;
517141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE:
518141cc406Sopenharmony_ci			o.name = "exposure";
519141cc406Sopenharmony_ci			o.title = "Exposure multiplier";
520141cc406Sopenharmony_ci			o.desc = "Exposure multiplier for all channels";
521141cc406Sopenharmony_ci			o.type = SANE_TYPE_FIXED;
522141cc406Sopenharmony_ci			o.unit = SANE_UNIT_NONE;
523141cc406Sopenharmony_ci			o.size = WSIZE;
524141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
525141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
526141cc406Sopenharmony_ci			range = (SANE_Range *)
527141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
528141cc406Sopenharmony_ci			if (!range)
529141cc406Sopenharmony_ci				alloc_failed = 1;
530141cc406Sopenharmony_ci			else {
531141cc406Sopenharmony_ci				range->min = SANE_FIX(0.);
532141cc406Sopenharmony_ci				range->max = SANE_FIX(10.);
533141cc406Sopenharmony_ci				range->quant = SANE_FIX(0.1);
534141cc406Sopenharmony_ci				o.constraint.range = range;
535141cc406Sopenharmony_ci			}
536141cc406Sopenharmony_ci			break;
537141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_R:
538141cc406Sopenharmony_ci			o.name = "red-exposure";
539141cc406Sopenharmony_ci			o.title = "Red exposure time";
540141cc406Sopenharmony_ci			o.desc = "Exposure time for red channel";
541141cc406Sopenharmony_ci			o.type = SANE_TYPE_FIXED;
542141cc406Sopenharmony_ci			o.unit = SANE_UNIT_MICROSECOND;
543141cc406Sopenharmony_ci			o.size = WSIZE;
544141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
545141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
546141cc406Sopenharmony_ci			range = (SANE_Range *)
547141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
548141cc406Sopenharmony_ci			if (!range)
549141cc406Sopenharmony_ci				alloc_failed = 1;
550141cc406Sopenharmony_ci			else {
551141cc406Sopenharmony_ci				range->min = SANE_FIX(50.);
552141cc406Sopenharmony_ci				range->max = SANE_FIX(20000.);
553141cc406Sopenharmony_ci				range->quant = SANE_FIX(10.);
554141cc406Sopenharmony_ci				o.constraint.range = range;
555141cc406Sopenharmony_ci			}
556141cc406Sopenharmony_ci			break;
557141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_G:
558141cc406Sopenharmony_ci			o.name = "green-exposure";
559141cc406Sopenharmony_ci			o.title = "Green exposure time";
560141cc406Sopenharmony_ci			o.desc = "Exposure time for green channel";
561141cc406Sopenharmony_ci			o.type = SANE_TYPE_FIXED;
562141cc406Sopenharmony_ci			o.unit = SANE_UNIT_MICROSECOND;
563141cc406Sopenharmony_ci			o.size = WSIZE;
564141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
565141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
566141cc406Sopenharmony_ci			range = (SANE_Range *)
567141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
568141cc406Sopenharmony_ci			if (!range)
569141cc406Sopenharmony_ci				alloc_failed = 1;
570141cc406Sopenharmony_ci			else {
571141cc406Sopenharmony_ci				range->min = SANE_FIX(50.);
572141cc406Sopenharmony_ci				range->max = SANE_FIX(20000.);
573141cc406Sopenharmony_ci				range->quant = SANE_FIX(10.);
574141cc406Sopenharmony_ci				o.constraint.range = range;
575141cc406Sopenharmony_ci			}
576141cc406Sopenharmony_ci			break;
577141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_B:
578141cc406Sopenharmony_ci			o.name = "blue-exposure";
579141cc406Sopenharmony_ci			o.title = "Blue exposure time";
580141cc406Sopenharmony_ci			o.desc = "Exposure time for blue channel";
581141cc406Sopenharmony_ci			o.type = SANE_TYPE_FIXED;
582141cc406Sopenharmony_ci			o.unit = SANE_UNIT_MICROSECOND;
583141cc406Sopenharmony_ci			o.size = WSIZE;
584141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
585141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
586141cc406Sopenharmony_ci			range = (SANE_Range *)
587141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
588141cc406Sopenharmony_ci			if (!range)
589141cc406Sopenharmony_ci				alloc_failed = 1;
590141cc406Sopenharmony_ci			else {
591141cc406Sopenharmony_ci				range->min = SANE_FIX(50.);
592141cc406Sopenharmony_ci				range->max = SANE_FIX(20000.);
593141cc406Sopenharmony_ci				range->quant = SANE_FIX(10.);
594141cc406Sopenharmony_ci				o.constraint.range = range;
595141cc406Sopenharmony_ci			}
596141cc406Sopenharmony_ci			break;
597141cc406Sopenharmony_ci		case CS3_OPTION_LUT_R:
598141cc406Sopenharmony_ci			o.name = "red-gamma-table";
599141cc406Sopenharmony_ci			o.title = "LUT for red channel";
600141cc406Sopenharmony_ci			o.desc = "LUT for red channel";
601141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
602141cc406Sopenharmony_ci			o.size = s->n_lut * WSIZE;
603141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
604141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
605141cc406Sopenharmony_ci			range = (SANE_Range *)
606141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
607141cc406Sopenharmony_ci			if (!range)
608141cc406Sopenharmony_ci				alloc_failed = 1;
609141cc406Sopenharmony_ci			else {
610141cc406Sopenharmony_ci				range->min = 0;
611141cc406Sopenharmony_ci				range->max = s->n_lut - 1;
612141cc406Sopenharmony_ci				range->quant = 1;
613141cc406Sopenharmony_ci				o.constraint.range = range;
614141cc406Sopenharmony_ci			}
615141cc406Sopenharmony_ci			break;
616141cc406Sopenharmony_ci		case CS3_OPTION_LUT_G:
617141cc406Sopenharmony_ci			o.name = "green-gamma-table";
618141cc406Sopenharmony_ci			o.title = "LUT for green channel";
619141cc406Sopenharmony_ci			o.desc = "LUT for green channel";
620141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
621141cc406Sopenharmony_ci			o.size = s->n_lut * WSIZE;
622141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
623141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
624141cc406Sopenharmony_ci			range = (SANE_Range *)
625141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
626141cc406Sopenharmony_ci			if (!range)
627141cc406Sopenharmony_ci				alloc_failed = 1;
628141cc406Sopenharmony_ci			else {
629141cc406Sopenharmony_ci				range->min = 0;
630141cc406Sopenharmony_ci				range->max = s->n_lut - 1;
631141cc406Sopenharmony_ci				range->quant = 1;
632141cc406Sopenharmony_ci				o.constraint.range = range;
633141cc406Sopenharmony_ci			}
634141cc406Sopenharmony_ci			break;
635141cc406Sopenharmony_ci		case CS3_OPTION_LUT_B:
636141cc406Sopenharmony_ci			o.name = "blue-gamma-table";
637141cc406Sopenharmony_ci			o.title = "LUT for blue channel";
638141cc406Sopenharmony_ci			o.desc = "LUT for blue channel";
639141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
640141cc406Sopenharmony_ci			o.size = s->n_lut * WSIZE;
641141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
642141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
643141cc406Sopenharmony_ci			range = (SANE_Range *)
644141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
645141cc406Sopenharmony_ci			if (!range)
646141cc406Sopenharmony_ci				alloc_failed = 1;
647141cc406Sopenharmony_ci			else {
648141cc406Sopenharmony_ci				range->min = 0;
649141cc406Sopenharmony_ci				range->max = s->n_lut - 1;
650141cc406Sopenharmony_ci				range->quant = 1;
651141cc406Sopenharmony_ci				o.constraint.range = range;
652141cc406Sopenharmony_ci			}
653141cc406Sopenharmony_ci			break;
654141cc406Sopenharmony_ci		case CS3_OPTION_LOAD:
655141cc406Sopenharmony_ci			o.name = "load";
656141cc406Sopenharmony_ci			o.title = "Load";
657141cc406Sopenharmony_ci			o.desc = "Load next slide";
658141cc406Sopenharmony_ci			o.type = SANE_TYPE_BUTTON;
659141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
660141cc406Sopenharmony_ci			if (s->n_frames > 1)
661141cc406Sopenharmony_ci				o.cap |= SANE_CAP_INACTIVE;
662141cc406Sopenharmony_ci			break;
663141cc406Sopenharmony_ci		case CS3_OPTION_AUTOLOAD:
664141cc406Sopenharmony_ci			o.name = "autoload";
665141cc406Sopenharmony_ci			o.title = "Autoload";
666141cc406Sopenharmony_ci			o.desc = "Autoload slide before each scan";
667141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
668141cc406Sopenharmony_ci			o.size = WSIZE;
669141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
670141cc406Sopenharmony_ci			if (s->n_frames > 1)
671141cc406Sopenharmony_ci				o.cap |= SANE_CAP_INACTIVE;
672141cc406Sopenharmony_ci			break;
673141cc406Sopenharmony_ci		case CS3_OPTION_EJECT:
674141cc406Sopenharmony_ci			o.name = "eject";
675141cc406Sopenharmony_ci			o.title = "Eject";
676141cc406Sopenharmony_ci			o.desc = "Eject loaded medium";
677141cc406Sopenharmony_ci			o.type = SANE_TYPE_BUTTON;
678141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
679141cc406Sopenharmony_ci			break;
680141cc406Sopenharmony_ci		case CS3_OPTION_RESET:
681141cc406Sopenharmony_ci			o.name = "reset";
682141cc406Sopenharmony_ci			o.title = "Reset scanner";
683141cc406Sopenharmony_ci			o.desc = "Initialize scanner";
684141cc406Sopenharmony_ci			o.type = SANE_TYPE_BUTTON;
685141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
686141cc406Sopenharmony_ci			break;
687141cc406Sopenharmony_ci		case CS3_OPTION_RESX:
688141cc406Sopenharmony_ci		case CS3_OPTION_RES:
689141cc406Sopenharmony_ci		case CS3_OPTION_PREVIEW_RESOLUTION:
690141cc406Sopenharmony_ci			if (i_option == CS3_OPTION_PREVIEW_RESOLUTION) {
691141cc406Sopenharmony_ci				o.name = "preview-resolution";
692141cc406Sopenharmony_ci				o.title = "Preview resolution";
693141cc406Sopenharmony_ci				o.desc = "Scanning resolution for preview mode in dpi, affecting both x and y directions";
694141cc406Sopenharmony_ci			} else if (i_option == CS3_OPTION_RES) {
695141cc406Sopenharmony_ci				o.name = "resolution";
696141cc406Sopenharmony_ci				o.title = "Resolution";
697141cc406Sopenharmony_ci				o.desc = "Scanning resolution in dpi, affecting both x and y directions";
698141cc406Sopenharmony_ci			} else {
699141cc406Sopenharmony_ci				o.name = "x-resolution";
700141cc406Sopenharmony_ci				o.title = "X resolution";
701141cc406Sopenharmony_ci				o.desc = "Scanning resolution in dpi, affecting x direction only";
702141cc406Sopenharmony_ci			}
703141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
704141cc406Sopenharmony_ci			o.unit = SANE_UNIT_DPI;
705141cc406Sopenharmony_ci			o.size = WSIZE;
706141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
707141cc406Sopenharmony_ci			if (i_option == CS3_OPTION_RESX)
708141cc406Sopenharmony_ci				o.cap |= SANE_CAP_INACTIVE |
709141cc406Sopenharmony_ci					SANE_CAP_ADVANCED;
710141cc406Sopenharmony_ci			if (i_option == CS3_OPTION_PREVIEW_RESOLUTION)
711141cc406Sopenharmony_ci				o.cap |= SANE_CAP_ADVANCED;
712141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
713141cc406Sopenharmony_ci			word_list =
714141cc406Sopenharmony_ci				(SANE_Word *) cs3_xmalloc((s->resx_n_list + 1)
715141cc406Sopenharmony_ci							  *
716141cc406Sopenharmony_ci							  sizeof(SANE_Word));
717141cc406Sopenharmony_ci			if (!word_list)
718141cc406Sopenharmony_ci				alloc_failed = 1;
719141cc406Sopenharmony_ci			else {
720141cc406Sopenharmony_ci				for (i_list = 0; i_list < s->resx_n_list;
721141cc406Sopenharmony_ci				     i_list++)
722141cc406Sopenharmony_ci					word_list[i_list + 1] =
723141cc406Sopenharmony_ci						s->resx_list[i_list];
724141cc406Sopenharmony_ci				word_list[0] = s->resx_n_list;
725141cc406Sopenharmony_ci				o.constraint.word_list = word_list;
726141cc406Sopenharmony_ci			}
727141cc406Sopenharmony_ci			break;
728141cc406Sopenharmony_ci		case CS3_OPTION_RESY:
729141cc406Sopenharmony_ci			o.name = "y-resolution";
730141cc406Sopenharmony_ci			o.title = "Y resolution";
731141cc406Sopenharmony_ci			o.desc = "Scanning resolution in dpi, affecting y direction only";
732141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
733141cc406Sopenharmony_ci			o.unit = SANE_UNIT_DPI;
734141cc406Sopenharmony_ci			o.size = WSIZE;
735141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
736141cc406Sopenharmony_ci				SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
737141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_WORD_LIST;
738141cc406Sopenharmony_ci			word_list =
739141cc406Sopenharmony_ci				(SANE_Word *) cs3_xmalloc((s->resy_n_list + 1)
740141cc406Sopenharmony_ci							  *
741141cc406Sopenharmony_ci							  sizeof(SANE_Word));
742141cc406Sopenharmony_ci			if (!word_list)
743141cc406Sopenharmony_ci				alloc_failed = 1;
744141cc406Sopenharmony_ci			else {
745141cc406Sopenharmony_ci				for (i_list = 0; i_list < s->resy_n_list;
746141cc406Sopenharmony_ci				     i_list++)
747141cc406Sopenharmony_ci					word_list[i_list + 1] =
748141cc406Sopenharmony_ci						s->resy_list[i_list];
749141cc406Sopenharmony_ci				word_list[0] = s->resy_n_list;
750141cc406Sopenharmony_ci				o.constraint.word_list = word_list;
751141cc406Sopenharmony_ci			}
752141cc406Sopenharmony_ci			break;
753141cc406Sopenharmony_ci		case CS3_OPTION_RES_INDEPENDENT:
754141cc406Sopenharmony_ci			o.name = "independent-res";
755141cc406Sopenharmony_ci			o.title = "Independent x/y resolutions";
756141cc406Sopenharmony_ci			o.desc = "Enable independent controls for scanning resolution in x and y direction";
757141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
758141cc406Sopenharmony_ci			o.size = WSIZE;
759141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
760141cc406Sopenharmony_ci				SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
761141cc406Sopenharmony_ci			break;
762141cc406Sopenharmony_ci		case CS3_OPTION_FRAME:
763141cc406Sopenharmony_ci			o.name = "frame";
764141cc406Sopenharmony_ci			o.title = "Frame number";
765141cc406Sopenharmony_ci			o.desc = "Number of frame to be scanned, starting with 1";
766141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
767141cc406Sopenharmony_ci			o.unit = SANE_UNIT_NONE;
768141cc406Sopenharmony_ci			o.size = WSIZE;
769141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
770141cc406Sopenharmony_ci			if (s->n_frames <= 1)
771141cc406Sopenharmony_ci				o.cap |= SANE_CAP_INACTIVE;
772141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
773141cc406Sopenharmony_ci			range = (SANE_Range *)
774141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
775141cc406Sopenharmony_ci			if (!range)
776141cc406Sopenharmony_ci				alloc_failed = 1;
777141cc406Sopenharmony_ci			else {
778141cc406Sopenharmony_ci				range->min = 1;
779141cc406Sopenharmony_ci				range->max = s->n_frames;
780141cc406Sopenharmony_ci				range->quant = 1;
781141cc406Sopenharmony_ci				o.constraint.range = range;
782141cc406Sopenharmony_ci			}
783141cc406Sopenharmony_ci			break;
784141cc406Sopenharmony_ci		case CS3_OPTION_FRAME_COUNT:
785141cc406Sopenharmony_ci			o.name = "frame-count";
786141cc406Sopenharmony_ci			o.title = "Frame count";
787141cc406Sopenharmony_ci			o.desc = "Amount of frames to scan";
788141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
789141cc406Sopenharmony_ci			o.unit = SANE_UNIT_NONE;
790141cc406Sopenharmony_ci			o.size = WSIZE;
791141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
792141cc406Sopenharmony_ci			if (s->n_frames <= 1)
793141cc406Sopenharmony_ci				o.cap |= SANE_CAP_INACTIVE;
794141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
795141cc406Sopenharmony_ci			range = (SANE_Range *)
796141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
797141cc406Sopenharmony_ci			if (!range)
798141cc406Sopenharmony_ci				alloc_failed = 1;
799141cc406Sopenharmony_ci			else {
800141cc406Sopenharmony_ci				range->min = 1;
801141cc406Sopenharmony_ci				range->max = s->n_frames - s->i_frame + 1;
802141cc406Sopenharmony_ci				range->quant = 1;
803141cc406Sopenharmony_ci				o.constraint.range = range;
804141cc406Sopenharmony_ci			}
805141cc406Sopenharmony_ci			break;
806141cc406Sopenharmony_ci		case CS3_OPTION_SUBFRAME:
807141cc406Sopenharmony_ci			o.name = "subframe";
808141cc406Sopenharmony_ci			o.title = "Frame shift";
809141cc406Sopenharmony_ci			o.desc = "Fine position within the selected frame";
810141cc406Sopenharmony_ci			o.type = SANE_TYPE_FIXED;
811141cc406Sopenharmony_ci			o.unit = SANE_UNIT_MM;
812141cc406Sopenharmony_ci			o.size = WSIZE;
813141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
814141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
815141cc406Sopenharmony_ci			range = (SANE_Range *)
816141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
817141cc406Sopenharmony_ci			if (!range)
818141cc406Sopenharmony_ci				alloc_failed = 1;
819141cc406Sopenharmony_ci			else {
820141cc406Sopenharmony_ci				range->min = SANE_FIX(0.);
821141cc406Sopenharmony_ci				range->max =
822141cc406Sopenharmony_ci					SANE_FIX((s->boundaryy -
823141cc406Sopenharmony_ci						  1) * s->unit_mm);
824141cc406Sopenharmony_ci				range->quant = SANE_FIX(0.);
825141cc406Sopenharmony_ci				o.constraint.range = range;
826141cc406Sopenharmony_ci			}
827141cc406Sopenharmony_ci			break;
828141cc406Sopenharmony_ci		case CS3_OPTION_XMIN:
829141cc406Sopenharmony_ci			o.name = "tl-x";
830141cc406Sopenharmony_ci			o.title = "Left x value of scan area";
831141cc406Sopenharmony_ci			o.desc = "Left x value of scan area";
832141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
833141cc406Sopenharmony_ci			o.unit = SANE_UNIT_PIXEL;
834141cc406Sopenharmony_ci			o.size = WSIZE;
835141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
836141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
837141cc406Sopenharmony_ci			if (!range)
838141cc406Sopenharmony_ci				alloc_failed = 1;
839141cc406Sopenharmony_ci			else {
840141cc406Sopenharmony_ci				range = (SANE_Range *)
841141cc406Sopenharmony_ci					cs3_xmalloc(sizeof(SANE_Range));
842141cc406Sopenharmony_ci				range->min = 0;
843141cc406Sopenharmony_ci				range->max = s->boundaryx - 1;
844141cc406Sopenharmony_ci				range->quant = 1;
845141cc406Sopenharmony_ci				o.constraint.range = range;
846141cc406Sopenharmony_ci			}
847141cc406Sopenharmony_ci			break;
848141cc406Sopenharmony_ci		case CS3_OPTION_XMAX:
849141cc406Sopenharmony_ci			o.name = "br-x";
850141cc406Sopenharmony_ci			o.title = "Right x value of scan area";
851141cc406Sopenharmony_ci			o.desc = "Right x value of scan area";
852141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
853141cc406Sopenharmony_ci			o.unit = SANE_UNIT_PIXEL;
854141cc406Sopenharmony_ci			o.size = WSIZE;
855141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
856141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
857141cc406Sopenharmony_ci			range = (SANE_Range *)
858141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
859141cc406Sopenharmony_ci			if (!range)
860141cc406Sopenharmony_ci				alloc_failed = 1;
861141cc406Sopenharmony_ci			else {
862141cc406Sopenharmony_ci				range->min = 0;
863141cc406Sopenharmony_ci				range->max = s->boundaryx - 1;
864141cc406Sopenharmony_ci				range->quant = 1;
865141cc406Sopenharmony_ci				o.constraint.range = range;
866141cc406Sopenharmony_ci			}
867141cc406Sopenharmony_ci			break;
868141cc406Sopenharmony_ci		case CS3_OPTION_YMIN:
869141cc406Sopenharmony_ci			o.name = "tl-y";
870141cc406Sopenharmony_ci			o.title = "Top y value of scan area";
871141cc406Sopenharmony_ci			o.desc = "Top y value of scan area";
872141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
873141cc406Sopenharmony_ci			o.unit = SANE_UNIT_PIXEL;
874141cc406Sopenharmony_ci			o.size = WSIZE;
875141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
876141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
877141cc406Sopenharmony_ci			range = (SANE_Range *)
878141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
879141cc406Sopenharmony_ci			if (!range)
880141cc406Sopenharmony_ci				alloc_failed = 1;
881141cc406Sopenharmony_ci			else {
882141cc406Sopenharmony_ci				range->min = 0;
883141cc406Sopenharmony_ci				range->max = s->boundaryy - 1;
884141cc406Sopenharmony_ci				range->quant = 1;
885141cc406Sopenharmony_ci				o.constraint.range = range;
886141cc406Sopenharmony_ci			}
887141cc406Sopenharmony_ci			break;
888141cc406Sopenharmony_ci		case CS3_OPTION_YMAX:
889141cc406Sopenharmony_ci			o.name = "br-y";
890141cc406Sopenharmony_ci			o.title = "Bottom y value of scan area";
891141cc406Sopenharmony_ci			o.desc = "Bottom y value of scan area";
892141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
893141cc406Sopenharmony_ci			o.unit = SANE_UNIT_PIXEL;
894141cc406Sopenharmony_ci			o.size = WSIZE;
895141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
896141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
897141cc406Sopenharmony_ci			range = (SANE_Range *)
898141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
899141cc406Sopenharmony_ci			if (!range)
900141cc406Sopenharmony_ci				alloc_failed = 1;
901141cc406Sopenharmony_ci			else {
902141cc406Sopenharmony_ci				range->min = 0;
903141cc406Sopenharmony_ci				range->max = s->boundaryy - 1;
904141cc406Sopenharmony_ci				range->quant = 1;
905141cc406Sopenharmony_ci				o.constraint.range = range;
906141cc406Sopenharmony_ci			}
907141cc406Sopenharmony_ci			break;
908141cc406Sopenharmony_ci		case CS3_OPTION_FOCUS_ON_CENTRE:
909141cc406Sopenharmony_ci			o.name = "focus-on-centre";
910141cc406Sopenharmony_ci			o.title = "Use centre of scan area as AF point";
911141cc406Sopenharmony_ci			o.desc = "Use centre of scan area as AF point instead of manual AF point selection";
912141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
913141cc406Sopenharmony_ci			o.size = WSIZE;
914141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
915141cc406Sopenharmony_ci			break;
916141cc406Sopenharmony_ci		case CS3_OPTION_FOCUS:
917141cc406Sopenharmony_ci			o.name = SANE_NAME_FOCUS;
918141cc406Sopenharmony_ci			o.title = SANE_TITLE_FOCUS;
919141cc406Sopenharmony_ci			o.desc = SANE_DESC_FOCUS;
920141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
921141cc406Sopenharmony_ci			o.unit = SANE_UNIT_NONE;
922141cc406Sopenharmony_ci			o.size = WSIZE;
923141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
924141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
925141cc406Sopenharmony_ci			range = (SANE_Range *)
926141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
927141cc406Sopenharmony_ci			if (!range)
928141cc406Sopenharmony_ci				alloc_failed = 1;
929141cc406Sopenharmony_ci			else {
930141cc406Sopenharmony_ci				range->min = s->focus_min;
931141cc406Sopenharmony_ci				range->max = s->focus_max;
932141cc406Sopenharmony_ci				range->quant = 1;
933141cc406Sopenharmony_ci				o.constraint.range = range;
934141cc406Sopenharmony_ci			}
935141cc406Sopenharmony_ci			break;
936141cc406Sopenharmony_ci		case CS3_OPTION_AUTOFOCUS:
937141cc406Sopenharmony_ci			o.name = SANE_NAME_AUTOFOCUS;
938141cc406Sopenharmony_ci			o.title = SANE_TITLE_AUTOFOCUS;
939141cc406Sopenharmony_ci			o.desc = SANE_DESC_AUTOFOCUS;
940141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
941141cc406Sopenharmony_ci			o.size = WSIZE;
942141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
943141cc406Sopenharmony_ci			break;
944141cc406Sopenharmony_ci		case CS3_OPTION_FOCUSX:
945141cc406Sopenharmony_ci			o.name = "focusx";
946141cc406Sopenharmony_ci			o.title = "X coordinate of AF point";
947141cc406Sopenharmony_ci			o.desc = "X coordinate of AF point";
948141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
949141cc406Sopenharmony_ci			o.unit = SANE_UNIT_PIXEL;
950141cc406Sopenharmony_ci			o.size = WSIZE;
951141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
952141cc406Sopenharmony_ci				SANE_CAP_INACTIVE;
953141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
954141cc406Sopenharmony_ci			range = (SANE_Range *)
955141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
956141cc406Sopenharmony_ci			if (!range)
957141cc406Sopenharmony_ci				alloc_failed = 1;
958141cc406Sopenharmony_ci			else {
959141cc406Sopenharmony_ci				range->min = 0;
960141cc406Sopenharmony_ci				range->max = s->boundaryx - 1;
961141cc406Sopenharmony_ci				range->quant = 1;
962141cc406Sopenharmony_ci				o.constraint.range = range;
963141cc406Sopenharmony_ci			}
964141cc406Sopenharmony_ci			break;
965141cc406Sopenharmony_ci		case CS3_OPTION_FOCUSY:
966141cc406Sopenharmony_ci			o.name = "focusy";
967141cc406Sopenharmony_ci			o.title = "Y coordinate of AF point";
968141cc406Sopenharmony_ci			o.desc = "Y coordinate of AF point";
969141cc406Sopenharmony_ci			o.type = SANE_TYPE_INT;
970141cc406Sopenharmony_ci			o.unit = SANE_UNIT_PIXEL;
971141cc406Sopenharmony_ci			o.size = WSIZE;
972141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
973141cc406Sopenharmony_ci				SANE_CAP_INACTIVE;
974141cc406Sopenharmony_ci			o.constraint_type = SANE_CONSTRAINT_RANGE;
975141cc406Sopenharmony_ci			range = (SANE_Range *)
976141cc406Sopenharmony_ci				cs3_xmalloc(sizeof(SANE_Range));
977141cc406Sopenharmony_ci			if (!range)
978141cc406Sopenharmony_ci				alloc_failed = 1;
979141cc406Sopenharmony_ci			else {
980141cc406Sopenharmony_ci				range->min = 0;
981141cc406Sopenharmony_ci				range->max = s->boundaryy - 1;
982141cc406Sopenharmony_ci				range->quant = 1;
983141cc406Sopenharmony_ci				o.constraint.range = range;
984141cc406Sopenharmony_ci			}
985141cc406Sopenharmony_ci			break;
986141cc406Sopenharmony_ci		case CS3_OPTION_SCAN_AE:
987141cc406Sopenharmony_ci			o.name = "ae";
988141cc406Sopenharmony_ci			o.title = "Auto-exposure";
989141cc406Sopenharmony_ci			o.desc = "Perform auto-exposure before scan";
990141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
991141cc406Sopenharmony_ci			o.size = WSIZE;
992141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
993141cc406Sopenharmony_ci			break;
994141cc406Sopenharmony_ci		case CS3_OPTION_SCAN_AE_WB:
995141cc406Sopenharmony_ci			o.name = "ae-wb";
996141cc406Sopenharmony_ci			o.title = "Auto-exposure with white balance";
997141cc406Sopenharmony_ci			o.desc = "Perform auto-exposure with white balance before scan";
998141cc406Sopenharmony_ci			o.type = SANE_TYPE_BOOL;
999141cc406Sopenharmony_ci			o.size = WSIZE;
1000141cc406Sopenharmony_ci			o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1001141cc406Sopenharmony_ci			break;
1002141cc406Sopenharmony_ci		default:
1003141cc406Sopenharmony_ci			DBG(1, "BUG: sane_open(): Unknown option number: %d\n", i_option);
1004141cc406Sopenharmony_ci			break;
1005141cc406Sopenharmony_ci		}
1006141cc406Sopenharmony_ci		s->option_list[i_option] = o;
1007141cc406Sopenharmony_ci	}
1008141cc406Sopenharmony_ci
1009141cc406Sopenharmony_ci	s->scanning = SANE_FALSE;
1010141cc406Sopenharmony_ci	s->preview = SANE_FALSE;
1011141cc406Sopenharmony_ci	s->negative = SANE_FALSE;
1012141cc406Sopenharmony_ci	s->autoload = SANE_FALSE;
1013141cc406Sopenharmony_ci	s->infrared = SANE_FALSE;
1014141cc406Sopenharmony_ci	s->ae = SANE_FALSE;
1015141cc406Sopenharmony_ci	s->aewb = SANE_FALSE;
1016141cc406Sopenharmony_ci	s->samples_per_scan = 1;
1017141cc406Sopenharmony_ci	s->depth = 8;
1018141cc406Sopenharmony_ci	s->i_frame = 1;
1019141cc406Sopenharmony_ci	s->frame_count = 1;
1020141cc406Sopenharmony_ci	s->subframe = 0.;
1021141cc406Sopenharmony_ci	s->res = s->resx = s->resx_max;
1022141cc406Sopenharmony_ci	s->resy = s->resy_max;
1023141cc406Sopenharmony_ci	s->res_independent = SANE_FALSE;
1024141cc406Sopenharmony_ci	s->res_preview = s->resx_max / 10;
1025141cc406Sopenharmony_ci	if (s->res_preview < s->resx_min)
1026141cc406Sopenharmony_ci		s->res_preview = s->resx_min;
1027141cc406Sopenharmony_ci	s->xmin = 0;
1028141cc406Sopenharmony_ci	s->xmax = s->boundaryx - 1;
1029141cc406Sopenharmony_ci	s->ymin = 0;
1030141cc406Sopenharmony_ci	s->ymax = s->boundaryy - 1;
1031141cc406Sopenharmony_ci	s->focus_on_centre = SANE_TRUE;
1032141cc406Sopenharmony_ci	s->focus = 0;
1033141cc406Sopenharmony_ci	s->focusx = 0;
1034141cc406Sopenharmony_ci	s->focusy = 0;
1035141cc406Sopenharmony_ci	s->exposure = 1.;
1036141cc406Sopenharmony_ci	s->exposure_r = 1200.;
1037141cc406Sopenharmony_ci	s->exposure_g = 1200.;
1038141cc406Sopenharmony_ci	s->exposure_b = 1000.;
1039141cc406Sopenharmony_ci	s->line_buf = NULL;
1040141cc406Sopenharmony_ci	s->n_line_buf = 0;
1041141cc406Sopenharmony_ci
1042141cc406Sopenharmony_ci	if (alloc_failed) {
1043141cc406Sopenharmony_ci		cs3_close(s);
1044141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
1045141cc406Sopenharmony_ci	}
1046141cc406Sopenharmony_ci
1047141cc406Sopenharmony_ci	return cs3_reserve_unit(s);
1048141cc406Sopenharmony_ci}
1049141cc406Sopenharmony_ci
1050141cc406Sopenharmony_civoid
1051141cc406Sopenharmony_cisane_close(SANE_Handle h)
1052141cc406Sopenharmony_ci{
1053141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1054141cc406Sopenharmony_ci
1055141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
1056141cc406Sopenharmony_ci
1057141cc406Sopenharmony_ci	cs3_release_unit(s);
1058141cc406Sopenharmony_ci	cs3_close(s);
1059141cc406Sopenharmony_ci}
1060141cc406Sopenharmony_ci
1061141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
1062141cc406Sopenharmony_cisane_get_option_descriptor(SANE_Handle h, SANE_Int n)
1063141cc406Sopenharmony_ci{
1064141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1065141cc406Sopenharmony_ci
1066141cc406Sopenharmony_ci	DBG(24, "%s, option %i\n", __func__, n);
1067141cc406Sopenharmony_ci
1068141cc406Sopenharmony_ci	if ((n >= 0) && (n < CS3_N_OPTIONS))
1069141cc406Sopenharmony_ci		return &s->option_list[n];
1070141cc406Sopenharmony_ci	else
1071141cc406Sopenharmony_ci		return NULL;
1072141cc406Sopenharmony_ci}
1073141cc406Sopenharmony_ci
1074141cc406Sopenharmony_ciSANE_Status
1075141cc406Sopenharmony_cisane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v,
1076141cc406Sopenharmony_ci		    SANE_Int * i)
1077141cc406Sopenharmony_ci{
1078141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1079141cc406Sopenharmony_ci	SANE_Int flags = 0;
1080141cc406Sopenharmony_ci	cs3_pixel_t pixel;
1081141cc406Sopenharmony_ci	SANE_Option_Descriptor o = s->option_list[n];
1082141cc406Sopenharmony_ci
1083141cc406Sopenharmony_ci	DBG(24, "%s, option %i, action %i.\n", __func__, n, a);
1084141cc406Sopenharmony_ci
1085141cc406Sopenharmony_ci	switch (a) {
1086141cc406Sopenharmony_ci	case SANE_ACTION_GET_VALUE:
1087141cc406Sopenharmony_ci
1088141cc406Sopenharmony_ci		switch (n) {
1089141cc406Sopenharmony_ci		case CS3_OPTION_NUM:
1090141cc406Sopenharmony_ci			*(SANE_Word *) v = CS3_N_OPTIONS;
1091141cc406Sopenharmony_ci			break;
1092141cc406Sopenharmony_ci		case CS3_OPTION_NEGATIVE:
1093141cc406Sopenharmony_ci			*(SANE_Word *) v = s->negative;
1094141cc406Sopenharmony_ci			break;
1095141cc406Sopenharmony_ci		case CS3_OPTION_INFRARED:
1096141cc406Sopenharmony_ci			*(SANE_Word *) v = s->infrared;
1097141cc406Sopenharmony_ci			break;
1098141cc406Sopenharmony_ci		case CS3_OPTION_SAMPLES_PER_SCAN:
1099141cc406Sopenharmony_ci			*(SANE_Word *) v = s->samples_per_scan;
1100141cc406Sopenharmony_ci			break;
1101141cc406Sopenharmony_ci		case CS3_OPTION_DEPTH:
1102141cc406Sopenharmony_ci			*(SANE_Word *) v = s->depth;
1103141cc406Sopenharmony_ci			break;
1104141cc406Sopenharmony_ci		case CS3_OPTION_PREVIEW:
1105141cc406Sopenharmony_ci			*(SANE_Word *) v = s->preview;
1106141cc406Sopenharmony_ci			break;
1107141cc406Sopenharmony_ci		case CS3_OPTION_AUTOLOAD:
1108141cc406Sopenharmony_ci			*(SANE_Word *) v = s->autoload;
1109141cc406Sopenharmony_ci			break;
1110141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE:
1111141cc406Sopenharmony_ci			*(SANE_Word *) v = SANE_FIX(s->exposure);
1112141cc406Sopenharmony_ci			break;
1113141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_R:
1114141cc406Sopenharmony_ci			*(SANE_Word *) v = SANE_FIX(s->exposure_r);
1115141cc406Sopenharmony_ci			break;
1116141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_G:
1117141cc406Sopenharmony_ci			*(SANE_Word *) v = SANE_FIX(s->exposure_g);
1118141cc406Sopenharmony_ci			break;
1119141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_B:
1120141cc406Sopenharmony_ci			*(SANE_Word *) v = SANE_FIX(s->exposure_b);
1121141cc406Sopenharmony_ci			break;
1122141cc406Sopenharmony_ci		case CS3_OPTION_LUT_R:
1123141cc406Sopenharmony_ci			if (!(s->lut_r))
1124141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1125141cc406Sopenharmony_ci			for (pixel = 0; pixel < s->n_lut; pixel++)
1126141cc406Sopenharmony_ci				((SANE_Word *) v)[pixel] = s->lut_r[pixel];
1127141cc406Sopenharmony_ci			break;
1128141cc406Sopenharmony_ci		case CS3_OPTION_LUT_G:
1129141cc406Sopenharmony_ci			if (!(s->lut_g))
1130141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1131141cc406Sopenharmony_ci			for (pixel = 0; pixel < s->n_lut; pixel++)
1132141cc406Sopenharmony_ci				((SANE_Word *) v)[pixel] = s->lut_g[pixel];
1133141cc406Sopenharmony_ci			break;
1134141cc406Sopenharmony_ci		case CS3_OPTION_LUT_B:
1135141cc406Sopenharmony_ci			if (!(s->lut_b))
1136141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1137141cc406Sopenharmony_ci			for (pixel = 0; pixel < s->n_lut; pixel++)
1138141cc406Sopenharmony_ci				((SANE_Word *) v)[pixel] = s->lut_b[pixel];
1139141cc406Sopenharmony_ci			break;
1140141cc406Sopenharmony_ci		case CS3_OPTION_EJECT:
1141141cc406Sopenharmony_ci			break;
1142141cc406Sopenharmony_ci		case CS3_OPTION_LOAD:
1143141cc406Sopenharmony_ci			break;
1144141cc406Sopenharmony_ci		case CS3_OPTION_RESET:
1145141cc406Sopenharmony_ci			break;
1146141cc406Sopenharmony_ci		case CS3_OPTION_FRAME:
1147141cc406Sopenharmony_ci			*(SANE_Word *) v = s->i_frame;
1148141cc406Sopenharmony_ci			break;
1149141cc406Sopenharmony_ci		case CS3_OPTION_FRAME_COUNT:
1150141cc406Sopenharmony_ci			*(SANE_Word *) v = s->frame_count;
1151141cc406Sopenharmony_ci			break;
1152141cc406Sopenharmony_ci		case CS3_OPTION_SUBFRAME:
1153141cc406Sopenharmony_ci			*(SANE_Word *) v = SANE_FIX(s->subframe);
1154141cc406Sopenharmony_ci			break;
1155141cc406Sopenharmony_ci		case CS3_OPTION_RES:
1156141cc406Sopenharmony_ci			*(SANE_Word *) v = s->res;
1157141cc406Sopenharmony_ci			break;
1158141cc406Sopenharmony_ci		case CS3_OPTION_RESX:
1159141cc406Sopenharmony_ci			*(SANE_Word *) v = s->resx;
1160141cc406Sopenharmony_ci			break;
1161141cc406Sopenharmony_ci		case CS3_OPTION_RESY:
1162141cc406Sopenharmony_ci			*(SANE_Word *) v = s->resy;
1163141cc406Sopenharmony_ci			break;
1164141cc406Sopenharmony_ci		case CS3_OPTION_RES_INDEPENDENT:
1165141cc406Sopenharmony_ci			*(SANE_Word *) v = s->res_independent;
1166141cc406Sopenharmony_ci			break;
1167141cc406Sopenharmony_ci		case CS3_OPTION_PREVIEW_RESOLUTION:
1168141cc406Sopenharmony_ci			*(SANE_Word *) v = s->res_preview;
1169141cc406Sopenharmony_ci			break;
1170141cc406Sopenharmony_ci		case CS3_OPTION_XMIN:
1171141cc406Sopenharmony_ci			*(SANE_Word *) v = s->xmin;
1172141cc406Sopenharmony_ci			break;
1173141cc406Sopenharmony_ci		case CS3_OPTION_XMAX:
1174141cc406Sopenharmony_ci			*(SANE_Word *) v = s->xmax;
1175141cc406Sopenharmony_ci			break;
1176141cc406Sopenharmony_ci		case CS3_OPTION_YMIN:
1177141cc406Sopenharmony_ci			*(SANE_Word *) v = s->ymin;
1178141cc406Sopenharmony_ci			break;
1179141cc406Sopenharmony_ci		case CS3_OPTION_YMAX:
1180141cc406Sopenharmony_ci			*(SANE_Word *) v = s->ymax;
1181141cc406Sopenharmony_ci			break;
1182141cc406Sopenharmony_ci		case CS3_OPTION_FOCUS_ON_CENTRE:
1183141cc406Sopenharmony_ci			*(SANE_Word *) v = s->focus_on_centre;
1184141cc406Sopenharmony_ci			break;
1185141cc406Sopenharmony_ci		case CS3_OPTION_FOCUS:
1186141cc406Sopenharmony_ci			*(SANE_Word *) v = s->focus;
1187141cc406Sopenharmony_ci			break;
1188141cc406Sopenharmony_ci		case CS3_OPTION_AUTOFOCUS:
1189141cc406Sopenharmony_ci			*(SANE_Word *) v = s->autofocus;
1190141cc406Sopenharmony_ci			break;
1191141cc406Sopenharmony_ci		case CS3_OPTION_FOCUSX:
1192141cc406Sopenharmony_ci			*(SANE_Word *) v = s->focusx;
1193141cc406Sopenharmony_ci			break;
1194141cc406Sopenharmony_ci		case CS3_OPTION_FOCUSY:
1195141cc406Sopenharmony_ci			*(SANE_Word *) v = s->focusy;
1196141cc406Sopenharmony_ci			break;
1197141cc406Sopenharmony_ci		case CS3_OPTION_SCAN_AE:
1198141cc406Sopenharmony_ci			*(SANE_Word *) v = s->ae;
1199141cc406Sopenharmony_ci			break;
1200141cc406Sopenharmony_ci		case CS3_OPTION_SCAN_AE_WB:
1201141cc406Sopenharmony_ci			*(SANE_Word *) v = s->aewb;
1202141cc406Sopenharmony_ci			break;
1203141cc406Sopenharmony_ci		default:
1204141cc406Sopenharmony_ci			DBG(4, "%s: Unknown option (bug?).\n", __func__);
1205141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
1206141cc406Sopenharmony_ci		}
1207141cc406Sopenharmony_ci		break;
1208141cc406Sopenharmony_ci
1209141cc406Sopenharmony_ci	case SANE_ACTION_SET_VALUE:
1210141cc406Sopenharmony_ci		if (s->scanning)
1211141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
1212141cc406Sopenharmony_ci		/* XXX do this for all elements of arrays */
1213141cc406Sopenharmony_ci		switch (o.type) {
1214141cc406Sopenharmony_ci		case SANE_TYPE_BOOL:
1215141cc406Sopenharmony_ci			if ((*(SANE_Word *) v != SANE_TRUE)
1216141cc406Sopenharmony_ci			    && (*(SANE_Word *) v != SANE_FALSE))
1217141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1218141cc406Sopenharmony_ci			break;
1219141cc406Sopenharmony_ci		case SANE_TYPE_INT:
1220141cc406Sopenharmony_ci		case SANE_TYPE_FIXED:
1221141cc406Sopenharmony_ci			switch (o.constraint_type) {
1222141cc406Sopenharmony_ci			case SANE_CONSTRAINT_RANGE:
1223141cc406Sopenharmony_ci				if (*(SANE_Word *) v <
1224141cc406Sopenharmony_ci				    o.constraint.range->min) {
1225141cc406Sopenharmony_ci					*(SANE_Word *) v =
1226141cc406Sopenharmony_ci						o.constraint.range->min;
1227141cc406Sopenharmony_ci					flags |= SANE_INFO_INEXACT;
1228141cc406Sopenharmony_ci				} else if (*(SANE_Word *) v >
1229141cc406Sopenharmony_ci					   o.constraint.range->max) {
1230141cc406Sopenharmony_ci					*(SANE_Word *) v =
1231141cc406Sopenharmony_ci						o.constraint.range->max;
1232141cc406Sopenharmony_ci					flags |= SANE_INFO_INEXACT;
1233141cc406Sopenharmony_ci				}
1234141cc406Sopenharmony_ci				break;
1235141cc406Sopenharmony_ci			case SANE_CONSTRAINT_WORD_LIST:
1236141cc406Sopenharmony_ci				break;
1237141cc406Sopenharmony_ci			default:
1238141cc406Sopenharmony_ci				break;
1239141cc406Sopenharmony_ci			}
1240141cc406Sopenharmony_ci			break;
1241141cc406Sopenharmony_ci		case SANE_TYPE_STRING:
1242141cc406Sopenharmony_ci			break;
1243141cc406Sopenharmony_ci		case SANE_TYPE_BUTTON:
1244141cc406Sopenharmony_ci			break;
1245141cc406Sopenharmony_ci		case SANE_TYPE_GROUP:
1246141cc406Sopenharmony_ci			break;
1247141cc406Sopenharmony_ci		}
1248141cc406Sopenharmony_ci		switch (n) {
1249141cc406Sopenharmony_ci		case CS3_OPTION_NUM:
1250141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
1251141cc406Sopenharmony_ci			break;
1252141cc406Sopenharmony_ci		case CS3_OPTION_NEGATIVE:
1253141cc406Sopenharmony_ci			s->negative = *(SANE_Word *) v;
1254141cc406Sopenharmony_ci			break;
1255141cc406Sopenharmony_ci		case CS3_OPTION_INFRARED:
1256141cc406Sopenharmony_ci			s->infrared = *(SANE_Word *) v;
1257141cc406Sopenharmony_ci			/*      flags |= SANE_INFO_RELOAD_PARAMS; XXX */
1258141cc406Sopenharmony_ci			break;
1259141cc406Sopenharmony_ci		case CS3_OPTION_SAMPLES_PER_SCAN:
1260141cc406Sopenharmony_ci			s->samples_per_scan = *(SANE_Word *) v;
1261141cc406Sopenharmony_ci			break;
1262141cc406Sopenharmony_ci		case CS3_OPTION_DEPTH:
1263141cc406Sopenharmony_ci			if (*(SANE_Word *) v > s->maxbits)
1264141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1265141cc406Sopenharmony_ci
1266141cc406Sopenharmony_ci			s->depth = *(SANE_Word *) v;
1267141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1268141cc406Sopenharmony_ci			break;
1269141cc406Sopenharmony_ci
1270141cc406Sopenharmony_ci		case CS3_OPTION_PREVIEW:
1271141cc406Sopenharmony_ci			s->preview = *(SANE_Word *) v;
1272141cc406Sopenharmony_ci			break;
1273141cc406Sopenharmony_ci
1274141cc406Sopenharmony_ci		case CS3_OPTION_AUTOLOAD:
1275141cc406Sopenharmony_ci			s->autoload = *(SANE_Word *) v;
1276141cc406Sopenharmony_ci			break;
1277141cc406Sopenharmony_ci
1278141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE:
1279141cc406Sopenharmony_ci			s->exposure = SANE_UNFIX(*(SANE_Word *) v);
1280141cc406Sopenharmony_ci			break;
1281141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_R:
1282141cc406Sopenharmony_ci			s->exposure_r = SANE_UNFIX(*(SANE_Word *) v);
1283141cc406Sopenharmony_ci			break;
1284141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_G:
1285141cc406Sopenharmony_ci			s->exposure_g = SANE_UNFIX(*(SANE_Word *) v);
1286141cc406Sopenharmony_ci			break;
1287141cc406Sopenharmony_ci		case CS3_OPTION_EXPOSURE_B:
1288141cc406Sopenharmony_ci			s->exposure_b = SANE_UNFIX(*(SANE_Word *) v);
1289141cc406Sopenharmony_ci			break;
1290141cc406Sopenharmony_ci		case CS3_OPTION_LUT_R:
1291141cc406Sopenharmony_ci			if (!(s->lut_r))
1292141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1293141cc406Sopenharmony_ci			for (pixel = 0; pixel < s->n_lut; pixel++)
1294141cc406Sopenharmony_ci				s->lut_r[pixel] = ((SANE_Word *) v)[pixel];
1295141cc406Sopenharmony_ci			break;
1296141cc406Sopenharmony_ci		case CS3_OPTION_LUT_G:
1297141cc406Sopenharmony_ci			if (!(s->lut_g))
1298141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1299141cc406Sopenharmony_ci			for (pixel = 0; pixel < s->n_lut; pixel++)
1300141cc406Sopenharmony_ci				s->lut_g[pixel] = ((SANE_Word *) v)[pixel];
1301141cc406Sopenharmony_ci			break;
1302141cc406Sopenharmony_ci		case CS3_OPTION_LUT_B:
1303141cc406Sopenharmony_ci			if (!(s->lut_b))
1304141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1305141cc406Sopenharmony_ci			for (pixel = 0; pixel < s->n_lut; pixel++)
1306141cc406Sopenharmony_ci				s->lut_b[pixel] = ((SANE_Word *) v)[pixel];
1307141cc406Sopenharmony_ci			break;
1308141cc406Sopenharmony_ci		case CS3_OPTION_LOAD:
1309141cc406Sopenharmony_ci			cs3_load(s);
1310141cc406Sopenharmony_ci			break;
1311141cc406Sopenharmony_ci		case CS3_OPTION_EJECT:
1312141cc406Sopenharmony_ci			cs3_eject(s);
1313141cc406Sopenharmony_ci			break;
1314141cc406Sopenharmony_ci		case CS3_OPTION_RESET:
1315141cc406Sopenharmony_ci			cs3_reset(s);
1316141cc406Sopenharmony_ci			break;
1317141cc406Sopenharmony_ci		case CS3_OPTION_FRAME:
1318141cc406Sopenharmony_ci			s->i_frame = *(SANE_Word *) v;
1319141cc406Sopenharmony_ci			break;
1320141cc406Sopenharmony_ci
1321141cc406Sopenharmony_ci		case CS3_OPTION_FRAME_COUNT:
1322141cc406Sopenharmony_ci			if (*(SANE_Word *) v > (s->n_frames - s->i_frame + 1))
1323141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1324141cc406Sopenharmony_ci			s->frame_count = *(SANE_Word *) v;
1325141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1326141cc406Sopenharmony_ci			break;
1327141cc406Sopenharmony_ci
1328141cc406Sopenharmony_ci		case CS3_OPTION_SUBFRAME:
1329141cc406Sopenharmony_ci			s->subframe = SANE_UNFIX(*(SANE_Word *) v);
1330141cc406Sopenharmony_ci			break;
1331141cc406Sopenharmony_ci		case CS3_OPTION_RES:
1332141cc406Sopenharmony_ci			s->res = *(SANE_Word *) v;
1333141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1334141cc406Sopenharmony_ci			break;
1335141cc406Sopenharmony_ci		case CS3_OPTION_RESX:
1336141cc406Sopenharmony_ci			s->resx = *(SANE_Word *) v;
1337141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1338141cc406Sopenharmony_ci			break;
1339141cc406Sopenharmony_ci		case CS3_OPTION_RESY:
1340141cc406Sopenharmony_ci			s->resy = *(SANE_Word *) v;
1341141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1342141cc406Sopenharmony_ci			break;
1343141cc406Sopenharmony_ci		case CS3_OPTION_RES_INDEPENDENT:
1344141cc406Sopenharmony_ci			s->res_independent = *(SANE_Word *) v;
1345141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1346141cc406Sopenharmony_ci			break;
1347141cc406Sopenharmony_ci		case CS3_OPTION_PREVIEW_RESOLUTION:
1348141cc406Sopenharmony_ci			s->res_preview = *(SANE_Word *) v;
1349141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1350141cc406Sopenharmony_ci			break;
1351141cc406Sopenharmony_ci		case CS3_OPTION_XMIN:
1352141cc406Sopenharmony_ci			s->xmin = *(SANE_Word *) v;
1353141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1354141cc406Sopenharmony_ci			break;
1355141cc406Sopenharmony_ci		case CS3_OPTION_XMAX:
1356141cc406Sopenharmony_ci			s->xmax = *(SANE_Word *) v;
1357141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1358141cc406Sopenharmony_ci			break;
1359141cc406Sopenharmony_ci		case CS3_OPTION_YMIN:
1360141cc406Sopenharmony_ci			s->ymin = *(SANE_Word *) v;
1361141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1362141cc406Sopenharmony_ci			break;
1363141cc406Sopenharmony_ci		case CS3_OPTION_YMAX:
1364141cc406Sopenharmony_ci			s->ymax = *(SANE_Word *) v;
1365141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_PARAMS;
1366141cc406Sopenharmony_ci			break;
1367141cc406Sopenharmony_ci		case CS3_OPTION_FOCUS_ON_CENTRE:
1368141cc406Sopenharmony_ci			s->focus_on_centre = *(SANE_Word *) v;
1369141cc406Sopenharmony_ci			if (s->focus_on_centre) {
1370141cc406Sopenharmony_ci				s->option_list[CS3_OPTION_FOCUSX].cap |=
1371141cc406Sopenharmony_ci					SANE_CAP_INACTIVE;
1372141cc406Sopenharmony_ci				s->option_list[CS3_OPTION_FOCUSY].cap |=
1373141cc406Sopenharmony_ci					SANE_CAP_INACTIVE;
1374141cc406Sopenharmony_ci			} else {
1375141cc406Sopenharmony_ci				s->option_list[CS3_OPTION_FOCUSX].cap &=
1376141cc406Sopenharmony_ci					~SANE_CAP_INACTIVE;
1377141cc406Sopenharmony_ci				s->option_list[CS3_OPTION_FOCUSY].cap &=
1378141cc406Sopenharmony_ci					~SANE_CAP_INACTIVE;
1379141cc406Sopenharmony_ci			}
1380141cc406Sopenharmony_ci			flags |= SANE_INFO_RELOAD_OPTIONS;
1381141cc406Sopenharmony_ci			break;
1382141cc406Sopenharmony_ci		case CS3_OPTION_FOCUS:
1383141cc406Sopenharmony_ci			s->focus = *(SANE_Word *) v;
1384141cc406Sopenharmony_ci			break;
1385141cc406Sopenharmony_ci		case CS3_OPTION_AUTOFOCUS:
1386141cc406Sopenharmony_ci			s->autofocus = *(SANE_Word *) v;
1387141cc406Sopenharmony_ci			break;
1388141cc406Sopenharmony_ci		case CS3_OPTION_FOCUSX:
1389141cc406Sopenharmony_ci			s->focusx = *(SANE_Word *) v;
1390141cc406Sopenharmony_ci			break;
1391141cc406Sopenharmony_ci		case CS3_OPTION_FOCUSY:
1392141cc406Sopenharmony_ci			s->focusy = *(SANE_Word *) v;
1393141cc406Sopenharmony_ci			break;
1394141cc406Sopenharmony_ci		case CS3_OPTION_SCAN_AE:
1395141cc406Sopenharmony_ci			s->ae = *(SANE_Word *) v;
1396141cc406Sopenharmony_ci			break;
1397141cc406Sopenharmony_ci		case CS3_OPTION_SCAN_AE_WB:
1398141cc406Sopenharmony_ci			s->aewb = *(SANE_Word *) v;
1399141cc406Sopenharmony_ci			break;
1400141cc406Sopenharmony_ci		default:
1401141cc406Sopenharmony_ci			DBG(4,
1402141cc406Sopenharmony_ci			    "Error: sane_control_option(): Unknown option number (bug?).\n");
1403141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
1404141cc406Sopenharmony_ci			break;
1405141cc406Sopenharmony_ci		}
1406141cc406Sopenharmony_ci		break;
1407141cc406Sopenharmony_ci
1408141cc406Sopenharmony_ci	default:
1409141cc406Sopenharmony_ci		DBG(1,
1410141cc406Sopenharmony_ci		    "BUG: sane_control_option(): Unknown action number.\n");
1411141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1412141cc406Sopenharmony_ci		break;
1413141cc406Sopenharmony_ci	}
1414141cc406Sopenharmony_ci
1415141cc406Sopenharmony_ci	if (i)
1416141cc406Sopenharmony_ci		*i = flags;
1417141cc406Sopenharmony_ci
1418141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1419141cc406Sopenharmony_ci}
1420141cc406Sopenharmony_ci
1421141cc406Sopenharmony_ciSANE_Status
1422141cc406Sopenharmony_cisane_get_parameters(SANE_Handle h, SANE_Parameters * p)
1423141cc406Sopenharmony_ci{
1424141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1425141cc406Sopenharmony_ci	SANE_Status status;
1426141cc406Sopenharmony_ci
1427141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
1428141cc406Sopenharmony_ci
1429141cc406Sopenharmony_ci	if (!s->scanning) {	/* only recalculate when not scanning */
1430141cc406Sopenharmony_ci		status = cs3_convert_options(s);
1431141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
1432141cc406Sopenharmony_ci			return status;
1433141cc406Sopenharmony_ci	}
1434141cc406Sopenharmony_ci
1435141cc406Sopenharmony_ci	p->bytes_per_line =
1436141cc406Sopenharmony_ci		s->n_colors * s->logical_width * s->bytes_per_pixel;
1437141cc406Sopenharmony_ci
1438141cc406Sopenharmony_ci#ifdef SANE_FRAME_RGBI
1439141cc406Sopenharmony_ci	if (s->infrared) {
1440141cc406Sopenharmony_ci		p->format = SANE_FRAME_RGBI;
1441141cc406Sopenharmony_ci
1442141cc406Sopenharmony_ci	} else {
1443141cc406Sopenharmony_ci#endif
1444141cc406Sopenharmony_ci		p->format = SANE_FRAME_RGB;	/* XXXXXXXX CCCCCCCCCC */
1445141cc406Sopenharmony_ci#ifdef SANE_FRAME_RGBI
1446141cc406Sopenharmony_ci	}
1447141cc406Sopenharmony_ci#endif
1448141cc406Sopenharmony_ci
1449141cc406Sopenharmony_ci	p->last_frame = SANE_TRUE;
1450141cc406Sopenharmony_ci	p->lines = s->logical_height;
1451141cc406Sopenharmony_ci	p->depth = 8 * s->bytes_per_pixel;
1452141cc406Sopenharmony_ci	p->pixels_per_line = s->logical_width;
1453141cc406Sopenharmony_ci
1454141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1455141cc406Sopenharmony_ci}
1456141cc406Sopenharmony_ci
1457141cc406Sopenharmony_ciSANE_Status
1458141cc406Sopenharmony_cisane_start(SANE_Handle h)
1459141cc406Sopenharmony_ci{
1460141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1461141cc406Sopenharmony_ci	SANE_Status status;
1462141cc406Sopenharmony_ci
1463141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
1464141cc406Sopenharmony_ci
1465141cc406Sopenharmony_ci	if (s->scanning)
1466141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1467141cc406Sopenharmony_ci
1468141cc406Sopenharmony_ci	if (s->n_frames > 1 && s->frame_count == 0) {
1469141cc406Sopenharmony_ci		DBG(4, "%s: no more frames\n", __func__);
1470141cc406Sopenharmony_ci		return SANE_STATUS_NO_DOCS;
1471141cc406Sopenharmony_ci	}
1472141cc406Sopenharmony_ci
1473141cc406Sopenharmony_ci	if (s->n_frames > 1) {
1474141cc406Sopenharmony_ci		DBG(4, "%s: scanning frame at position %d, %d to go\n",
1475141cc406Sopenharmony_ci		    __func__, s->i_frame, s->frame_count);
1476141cc406Sopenharmony_ci	}
1477141cc406Sopenharmony_ci
1478141cc406Sopenharmony_ci	status = cs3_convert_options(s);
1479141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1480141cc406Sopenharmony_ci		return status;
1481141cc406Sopenharmony_ci
1482141cc406Sopenharmony_ci	s->i_line_buf = 0;
1483141cc406Sopenharmony_ci	s->xfer_position = 0;
1484141cc406Sopenharmony_ci
1485141cc406Sopenharmony_ci	s->scanning = SANE_TRUE;
1486141cc406Sopenharmony_ci
1487141cc406Sopenharmony_ci	/* load if appropriate */
1488141cc406Sopenharmony_ci	if (s->autoload) {
1489141cc406Sopenharmony_ci		status = cs3_load(s);
1490141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
1491141cc406Sopenharmony_ci			return status;
1492141cc406Sopenharmony_ci	}
1493141cc406Sopenharmony_ci
1494141cc406Sopenharmony_ci	/* check for documents */
1495141cc406Sopenharmony_ci	status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
1496141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1497141cc406Sopenharmony_ci		return status;
1498141cc406Sopenharmony_ci	if (s->status & CS3_STATUS_NO_DOCS)
1499141cc406Sopenharmony_ci		return SANE_STATUS_NO_DOCS;
1500141cc406Sopenharmony_ci
1501141cc406Sopenharmony_ci	if (s->autofocus) {
1502141cc406Sopenharmony_ci		status = cs3_autofocus(s);
1503141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
1504141cc406Sopenharmony_ci			return status;
1505141cc406Sopenharmony_ci	}
1506141cc406Sopenharmony_ci
1507141cc406Sopenharmony_ci	if (s->aewb) {
1508141cc406Sopenharmony_ci		status = cs3_autoexposure(s, 1);
1509141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
1510141cc406Sopenharmony_ci			return status;
1511141cc406Sopenharmony_ci	} else if (s->ae) {
1512141cc406Sopenharmony_ci		status = cs3_autoexposure(s, 0);
1513141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
1514141cc406Sopenharmony_ci			return status;
1515141cc406Sopenharmony_ci	}
1516141cc406Sopenharmony_ci
1517141cc406Sopenharmony_ci	return cs3_scan(s, CS3_SCAN_NORMAL);
1518141cc406Sopenharmony_ci}
1519141cc406Sopenharmony_ci
1520141cc406Sopenharmony_ciSANE_Status
1521141cc406Sopenharmony_cisane_read(SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
1522141cc406Sopenharmony_ci{
1523141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1524141cc406Sopenharmony_ci	SANE_Status status;
1525141cc406Sopenharmony_ci	ssize_t xfer_len_in, xfer_len_line, xfer_len_out;
1526141cc406Sopenharmony_ci	unsigned long index;
1527141cc406Sopenharmony_ci	int color, sample_pass;
1528141cc406Sopenharmony_ci	uint8_t *s8 = NULL;
1529141cc406Sopenharmony_ci	uint16_t *s16 = NULL;
1530141cc406Sopenharmony_ci	double m_avg_sum;
1531141cc406Sopenharmony_ci	SANE_Byte *line_buf_new;
1532141cc406Sopenharmony_ci
1533141cc406Sopenharmony_ci	DBG(32, "%s, maxlen = %i.\n", __func__, maxlen);
1534141cc406Sopenharmony_ci
1535141cc406Sopenharmony_ci	if (!s->scanning) {
1536141cc406Sopenharmony_ci		*len = 0;
1537141cc406Sopenharmony_ci		return SANE_STATUS_CANCELLED;
1538141cc406Sopenharmony_ci	}
1539141cc406Sopenharmony_ci
1540141cc406Sopenharmony_ci	/* transfer from buffer */
1541141cc406Sopenharmony_ci	if (s->i_line_buf > 0) {
1542141cc406Sopenharmony_ci		xfer_len_out = s->n_line_buf - s->i_line_buf;
1543141cc406Sopenharmony_ci		if (xfer_len_out > maxlen)
1544141cc406Sopenharmony_ci			xfer_len_out = maxlen;
1545141cc406Sopenharmony_ci
1546141cc406Sopenharmony_ci		memcpy(buf, &(s->line_buf[s->i_line_buf]), xfer_len_out);
1547141cc406Sopenharmony_ci
1548141cc406Sopenharmony_ci		s->i_line_buf += xfer_len_out;
1549141cc406Sopenharmony_ci		if (s->i_line_buf >= s->n_line_buf)
1550141cc406Sopenharmony_ci			s->i_line_buf = 0;
1551141cc406Sopenharmony_ci
1552141cc406Sopenharmony_ci		*len = xfer_len_out;
1553141cc406Sopenharmony_ci		return SANE_STATUS_GOOD;
1554141cc406Sopenharmony_ci	}
1555141cc406Sopenharmony_ci
1556141cc406Sopenharmony_ci	xfer_len_line = s->n_colors * s->logical_width * s->bytes_per_pixel;
1557141cc406Sopenharmony_ci	xfer_len_in = xfer_len_line + (s->n_colors * s->odd_padding);
1558141cc406Sopenharmony_ci
1559141cc406Sopenharmony_ci	if ((xfer_len_in & 0x3f)) {
1560141cc406Sopenharmony_ci		int d = ((xfer_len_in / 512) * 512) + 512;
1561141cc406Sopenharmony_ci		s->block_padding = d - xfer_len_in;
1562141cc406Sopenharmony_ci	}
1563141cc406Sopenharmony_ci
1564141cc406Sopenharmony_ci	DBG(22, "%s: block_padding = %d, odd_padding = %d\n",
1565141cc406Sopenharmony_ci	    __func__, s->block_padding, s->odd_padding);
1566141cc406Sopenharmony_ci
1567141cc406Sopenharmony_ci	DBG(22,
1568141cc406Sopenharmony_ci	    "%s: colors = %d, logical_width = %ld, bytes_per_pixel = %d\n",
1569141cc406Sopenharmony_ci	    __func__, s->n_colors, s->logical_width, s->bytes_per_pixel);
1570141cc406Sopenharmony_ci
1571141cc406Sopenharmony_ci
1572141cc406Sopenharmony_ci	/* Do not change the behaviour of older models, pad to 512 */
1573141cc406Sopenharmony_ci	if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) {
1574141cc406Sopenharmony_ci		xfer_len_in += s->block_padding;
1575141cc406Sopenharmony_ci		if (xfer_len_in & 0x3f)
1576141cc406Sopenharmony_ci			DBG(1, "BUG: %s, not a multiple of 64. (0x%06lx)\n",
1577141cc406Sopenharmony_ci			    __func__, (long) xfer_len_in);
1578141cc406Sopenharmony_ci	}
1579141cc406Sopenharmony_ci
1580141cc406Sopenharmony_ci	if (s->xfer_position + xfer_len_line > s->xfer_bytes_total)
1581141cc406Sopenharmony_ci		xfer_len_line = s->xfer_bytes_total - s->xfer_position;	/* just in case */
1582141cc406Sopenharmony_ci
1583141cc406Sopenharmony_ci	if (xfer_len_line == 0) {	/* no more data */
1584141cc406Sopenharmony_ci		*len = 0;
1585141cc406Sopenharmony_ci
1586141cc406Sopenharmony_ci		/* increment frame number if appropriate */
1587141cc406Sopenharmony_ci		if (s->n_frames > 1 && --s->frame_count) {
1588141cc406Sopenharmony_ci			s->i_frame++;
1589141cc406Sopenharmony_ci		}
1590141cc406Sopenharmony_ci
1591141cc406Sopenharmony_ci		s->scanning = SANE_FALSE;
1592141cc406Sopenharmony_ci		return SANE_STATUS_EOF;
1593141cc406Sopenharmony_ci	}
1594141cc406Sopenharmony_ci
1595141cc406Sopenharmony_ci	if (xfer_len_line != s->n_line_buf) {
1596141cc406Sopenharmony_ci		line_buf_new =
1597141cc406Sopenharmony_ci			(SANE_Byte *) cs3_xrealloc(s->line_buf,
1598141cc406Sopenharmony_ci						   xfer_len_line *
1599141cc406Sopenharmony_ci						   sizeof(SANE_Byte));
1600141cc406Sopenharmony_ci		if (!line_buf_new) {
1601141cc406Sopenharmony_ci			*len = 0;
1602141cc406Sopenharmony_ci			return SANE_STATUS_NO_MEM;
1603141cc406Sopenharmony_ci		}
1604141cc406Sopenharmony_ci		s->line_buf = line_buf_new;
1605141cc406Sopenharmony_ci		s->n_line_buf = xfer_len_line;
1606141cc406Sopenharmony_ci	}
1607141cc406Sopenharmony_ci
1608141cc406Sopenharmony_ci	/* adapt for multi-sampling */
1609141cc406Sopenharmony_ci	xfer_len_in *= s->samples_per_scan;
1610141cc406Sopenharmony_ci
1611141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_READY);
1612141cc406Sopenharmony_ci	cs3_init_buffer(s);
1613141cc406Sopenharmony_ci	cs3_parse_cmd(s, "28 00 00 00 00 00");
1614141cc406Sopenharmony_ci	cs3_pack_byte(s, (xfer_len_in >> 16) & 0xff);
1615141cc406Sopenharmony_ci	cs3_pack_byte(s, (xfer_len_in >> 8) & 0xff);
1616141cc406Sopenharmony_ci	cs3_pack_byte(s, xfer_len_in & 0xff);
1617141cc406Sopenharmony_ci	cs3_parse_cmd(s, "00");
1618141cc406Sopenharmony_ci	s->n_recv = xfer_len_in;
1619141cc406Sopenharmony_ci
1620141cc406Sopenharmony_ci	status = cs3_issue_cmd(s);
1621141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
1622141cc406Sopenharmony_ci		*len = 0;
1623141cc406Sopenharmony_ci		return status;
1624141cc406Sopenharmony_ci	}
1625141cc406Sopenharmony_ci
1626141cc406Sopenharmony_ci	for (index = 0; index < s->logical_width; index++) {
1627141cc406Sopenharmony_ci		for (color = 0; color < s->n_colors; color++) {
1628141cc406Sopenharmony_ci			int where = s->bytes_per_pixel
1629141cc406Sopenharmony_ci				* (s->n_colors * index + color);
1630141cc406Sopenharmony_ci
1631141cc406Sopenharmony_ci			m_avg_sum = 0.0;
1632141cc406Sopenharmony_ci
1633141cc406Sopenharmony_ci			switch (s->bytes_per_pixel) {
1634141cc406Sopenharmony_ci			case 1:
1635141cc406Sopenharmony_ci			{
1636141cc406Sopenharmony_ci				/* target address */
1637141cc406Sopenharmony_ci				s8 = (uint8_t *) & (s->line_buf[where]);
1638141cc406Sopenharmony_ci
1639141cc406Sopenharmony_ci				if (s->samples_per_scan > 1) {
1640141cc406Sopenharmony_ci					/* calculate average of multi samples */
1641141cc406Sopenharmony_ci					for (sample_pass = 0;
1642141cc406Sopenharmony_ci							sample_pass < s->samples_per_scan;
1643141cc406Sopenharmony_ci							sample_pass++) {
1644141cc406Sopenharmony_ci						/* source index */
1645141cc406Sopenharmony_ci						int p8 = (sample_pass * s->n_colors + color)
1646141cc406Sopenharmony_ci							* s->logical_width
1647141cc406Sopenharmony_ci							+ (color + 1) * s->odd_padding
1648141cc406Sopenharmony_ci							+ index;
1649141cc406Sopenharmony_ci						m_avg_sum += (double) s->recv_buf[p8];
1650141cc406Sopenharmony_ci					}
1651141cc406Sopenharmony_ci					*s8 = (uint8_t) (m_avg_sum / s->samples_per_scan + 0.5);
1652141cc406Sopenharmony_ci				} else {
1653141cc406Sopenharmony_ci					/* shortcut for single sample */
1654141cc406Sopenharmony_ci					int p8 = s->logical_width * color
1655141cc406Sopenharmony_ci						+ (color + 1) * s->odd_padding
1656141cc406Sopenharmony_ci						+ index;
1657141cc406Sopenharmony_ci					*s8 = s->recv_buf[p8];
1658141cc406Sopenharmony_ci				}
1659141cc406Sopenharmony_ci			}
1660141cc406Sopenharmony_ci				break;
1661141cc406Sopenharmony_ci			case 2:
1662141cc406Sopenharmony_ci			{
1663141cc406Sopenharmony_ci				/* target address */
1664141cc406Sopenharmony_ci				s16 = (uint16_t *) & (s->line_buf[where]);
1665141cc406Sopenharmony_ci
1666141cc406Sopenharmony_ci				if (s->samples_per_scan > 1) {
1667141cc406Sopenharmony_ci					/* calculate average of multi samples */
1668141cc406Sopenharmony_ci					for (sample_pass = 0;
1669141cc406Sopenharmony_ci							sample_pass < s->samples_per_scan;
1670141cc406Sopenharmony_ci							sample_pass++) {
1671141cc406Sopenharmony_ci						/* source index */
1672141cc406Sopenharmony_ci						int p16 = 2 * ((sample_pass * s->n_colors + color)
1673141cc406Sopenharmony_ci								* s->logical_width + index);
1674141cc406Sopenharmony_ci						m_avg_sum += (double) ((s->recv_buf[p16] << 8)
1675141cc406Sopenharmony_ci							+ s->recv_buf[p16 + 1]);
1676141cc406Sopenharmony_ci					}
1677141cc406Sopenharmony_ci					*s16 = (uint16_t) (m_avg_sum / s->samples_per_scan + 0.5);
1678141cc406Sopenharmony_ci				} else {
1679141cc406Sopenharmony_ci					/* shortcut for single sample */
1680141cc406Sopenharmony_ci					int p16 = 2 * (color * s->logical_width + index);
1681141cc406Sopenharmony_ci
1682141cc406Sopenharmony_ci					*s16 = (s->recv_buf[p16] << 8)
1683141cc406Sopenharmony_ci						+ s->recv_buf[p16 + 1];
1684141cc406Sopenharmony_ci				}
1685141cc406Sopenharmony_ci
1686141cc406Sopenharmony_ci				*s16 <<= s->shift_bits;
1687141cc406Sopenharmony_ci			}
1688141cc406Sopenharmony_ci				break;
1689141cc406Sopenharmony_ci
1690141cc406Sopenharmony_ci			default:
1691141cc406Sopenharmony_ci				DBG(1,
1692141cc406Sopenharmony_ci				    "BUG: sane_read(): Unknown number of bytes per pixel.\n");
1693141cc406Sopenharmony_ci				*len = 0;
1694141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
1695141cc406Sopenharmony_ci				break;
1696141cc406Sopenharmony_ci			}
1697141cc406Sopenharmony_ci		}
1698141cc406Sopenharmony_ci	}
1699141cc406Sopenharmony_ci
1700141cc406Sopenharmony_ci	s->xfer_position += xfer_len_line;
1701141cc406Sopenharmony_ci
1702141cc406Sopenharmony_ci	xfer_len_out = xfer_len_line;
1703141cc406Sopenharmony_ci	if (xfer_len_out > maxlen)
1704141cc406Sopenharmony_ci		xfer_len_out = maxlen;
1705141cc406Sopenharmony_ci
1706141cc406Sopenharmony_ci	memcpy(buf, s->line_buf, xfer_len_out);
1707141cc406Sopenharmony_ci	if (xfer_len_out < xfer_len_line)
1708141cc406Sopenharmony_ci		s->i_line_buf = xfer_len_out;	/* data left in the line buffer, read out next time */
1709141cc406Sopenharmony_ci
1710141cc406Sopenharmony_ci	*len = xfer_len_out;
1711141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1712141cc406Sopenharmony_ci}
1713141cc406Sopenharmony_ci
1714141cc406Sopenharmony_civoid
1715141cc406Sopenharmony_cisane_cancel(SANE_Handle h)
1716141cc406Sopenharmony_ci{
1717141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1718141cc406Sopenharmony_ci
1719141cc406Sopenharmony_ci	DBG(10, "%s, scanning = %d.\n", __func__, s->scanning);
1720141cc406Sopenharmony_ci
1721141cc406Sopenharmony_ci	if (s->scanning) {
1722141cc406Sopenharmony_ci		cs3_init_buffer(s);
1723141cc406Sopenharmony_ci		cs3_parse_cmd(s, "c0 00 00 00 00 00");
1724141cc406Sopenharmony_ci		cs3_issue_cmd(s);
1725141cc406Sopenharmony_ci	}
1726141cc406Sopenharmony_ci
1727141cc406Sopenharmony_ci	s->scanning = SANE_FALSE;
1728141cc406Sopenharmony_ci}
1729141cc406Sopenharmony_ci
1730141cc406Sopenharmony_ciSANE_Status
1731141cc406Sopenharmony_cisane_set_io_mode(SANE_Handle h, SANE_Bool m)
1732141cc406Sopenharmony_ci{
1733141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1734141cc406Sopenharmony_ci
1735141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
1736141cc406Sopenharmony_ci
1737141cc406Sopenharmony_ci	if (!s->scanning)
1738141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1739141cc406Sopenharmony_ci	if (m == SANE_FALSE)
1740141cc406Sopenharmony_ci		return SANE_STATUS_GOOD;
1741141cc406Sopenharmony_ci	else
1742141cc406Sopenharmony_ci		return SANE_STATUS_UNSUPPORTED;
1743141cc406Sopenharmony_ci}
1744141cc406Sopenharmony_ci
1745141cc406Sopenharmony_ciSANE_Status
1746141cc406Sopenharmony_cisane_get_select_fd(SANE_Handle h, SANE_Int * fd)
1747141cc406Sopenharmony_ci{
1748141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) h;
1749141cc406Sopenharmony_ci
1750141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
1751141cc406Sopenharmony_ci
1752141cc406Sopenharmony_ci	(void) fd;		/* to shut up compiler */
1753141cc406Sopenharmony_ci	(void) s;		/* to shut up compiler */
1754141cc406Sopenharmony_ci
1755141cc406Sopenharmony_ci	return SANE_STATUS_UNSUPPORTED;
1756141cc406Sopenharmony_ci}
1757141cc406Sopenharmony_ci
1758141cc406Sopenharmony_ci
1759141cc406Sopenharmony_ci/* ========================================================================= */
1760141cc406Sopenharmony_ci/* private functions */
1761141cc406Sopenharmony_ci
1762141cc406Sopenharmony_cistatic void
1763141cc406Sopenharmony_cics3_trim(char *s)
1764141cc406Sopenharmony_ci{
1765141cc406Sopenharmony_ci	int i, l = strlen(s);
1766141cc406Sopenharmony_ci
1767141cc406Sopenharmony_ci	for (i = l - 1; i > 0; i--) {
1768141cc406Sopenharmony_ci		if (s[i] == ' ')
1769141cc406Sopenharmony_ci			s[i] = '\0';
1770141cc406Sopenharmony_ci		else
1771141cc406Sopenharmony_ci			break;
1772141cc406Sopenharmony_ci	}
1773141cc406Sopenharmony_ci}
1774141cc406Sopenharmony_ci
1775141cc406Sopenharmony_cistatic SANE_Status
1776141cc406Sopenharmony_cics3_open(const char *device, cs3_interface_t interface, cs3_t ** sp)
1777141cc406Sopenharmony_ci{
1778141cc406Sopenharmony_ci	SANE_Status status;
1779141cc406Sopenharmony_ci	cs3_t *s;
1780141cc406Sopenharmony_ci	char *prefix = NULL, *line;
1781141cc406Sopenharmony_ci	int i;
1782141cc406Sopenharmony_ci	int alloc_failed = 0;
1783141cc406Sopenharmony_ci	SANE_Device **device_list_new;
1784141cc406Sopenharmony_ci
1785141cc406Sopenharmony_ci	DBG(6, "%s, device = %s, interface = %i\n",
1786141cc406Sopenharmony_ci	    __func__, device, interface);
1787141cc406Sopenharmony_ci
1788141cc406Sopenharmony_ci	if (!strncmp(device, "auto", 5)) {
1789141cc406Sopenharmony_ci		try_interface = CS3_INTERFACE_SCSI;
1790141cc406Sopenharmony_ci		sanei_config_attach_matching_devices("scsi Nikon *",
1791141cc406Sopenharmony_ci						     cs3_attach);
1792141cc406Sopenharmony_ci		try_interface = CS3_INTERFACE_USB;
1793141cc406Sopenharmony_ci		sanei_usb_attach_matching_devices("usb 0x04b0 0x4000",
1794141cc406Sopenharmony_ci						  cs3_attach);
1795141cc406Sopenharmony_ci		sanei_usb_attach_matching_devices("usb 0x04b0 0x4001",
1796141cc406Sopenharmony_ci						  cs3_attach);
1797141cc406Sopenharmony_ci		sanei_usb_attach_matching_devices("usb 0x04b0 0x4002",
1798141cc406Sopenharmony_ci						  cs3_attach);
1799141cc406Sopenharmony_ci		return SANE_STATUS_GOOD;
1800141cc406Sopenharmony_ci	}
1801141cc406Sopenharmony_ci
1802141cc406Sopenharmony_ci	if ((s = (cs3_t *) cs3_xmalloc(sizeof(cs3_t))) == NULL)
1803141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
1804141cc406Sopenharmony_ci	memset(s, 0, sizeof(cs3_t));
1805141cc406Sopenharmony_ci
1806141cc406Sopenharmony_ci	/* fill magic bits */
1807141cc406Sopenharmony_ci	s->magic = SANE_COOKIE;
1808141cc406Sopenharmony_ci	s->cookie_ptr = &s->cookie;
1809141cc406Sopenharmony_ci
1810141cc406Sopenharmony_ci	s->cookie.version = 0x01;
1811141cc406Sopenharmony_ci	s->cookie.vendor = s->vendor_string;
1812141cc406Sopenharmony_ci	s->cookie.model = s->product_string;
1813141cc406Sopenharmony_ci	s->cookie.revision = s->revision_string;
1814141cc406Sopenharmony_ci
1815141cc406Sopenharmony_ci	s->send_buf = s->recv_buf = NULL;
1816141cc406Sopenharmony_ci	s->send_buf_size = s->recv_buf_size = 0;
1817141cc406Sopenharmony_ci
1818141cc406Sopenharmony_ci	switch (interface) {
1819141cc406Sopenharmony_ci	case CS3_INTERFACE_UNKNOWN:
1820141cc406Sopenharmony_ci		for (i = 0; i < 2; i++) {
1821141cc406Sopenharmony_ci			switch (i) {
1822141cc406Sopenharmony_ci			case 1:
1823141cc406Sopenharmony_ci				prefix = "usb:";
1824141cc406Sopenharmony_ci				try_interface = CS3_INTERFACE_USB;
1825141cc406Sopenharmony_ci				break;
1826141cc406Sopenharmony_ci			default:
1827141cc406Sopenharmony_ci				prefix = "scsi:";
1828141cc406Sopenharmony_ci				try_interface = CS3_INTERFACE_SCSI;
1829141cc406Sopenharmony_ci				break;
1830141cc406Sopenharmony_ci			}
1831141cc406Sopenharmony_ci			if (!strncmp(device, prefix, strlen(prefix))) {
1832141cc406Sopenharmony_ci				const void *p = device + strlen(prefix);
1833141cc406Sopenharmony_ci				cs3_xfree(s);
1834141cc406Sopenharmony_ci				return cs3_open(p, try_interface, sp);
1835141cc406Sopenharmony_ci			}
1836141cc406Sopenharmony_ci		}
1837141cc406Sopenharmony_ci		cs3_xfree(s);
1838141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1839141cc406Sopenharmony_ci		break;
1840141cc406Sopenharmony_ci	case CS3_INTERFACE_SCSI:
1841141cc406Sopenharmony_ci		s->interface = CS3_INTERFACE_SCSI;
1842141cc406Sopenharmony_ci		DBG(6,
1843141cc406Sopenharmony_ci		    "%s, trying to open %s, assuming SCSI or SBP2 interface\n",
1844141cc406Sopenharmony_ci		    __func__, device);
1845141cc406Sopenharmony_ci		status = sanei_scsi_open(device, &s->fd,
1846141cc406Sopenharmony_ci					 cs3_scsi_sense_handler, s);
1847141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD) {
1848141cc406Sopenharmony_ci			DBG(6, " ...failed: %s.\n", sane_strstatus(status));
1849141cc406Sopenharmony_ci			cs3_xfree(s);
1850141cc406Sopenharmony_ci			return status;
1851141cc406Sopenharmony_ci		}
1852141cc406Sopenharmony_ci		break;
1853141cc406Sopenharmony_ci	case CS3_INTERFACE_USB:
1854141cc406Sopenharmony_ci		s->interface = CS3_INTERFACE_USB;
1855141cc406Sopenharmony_ci		DBG(6, "%s, trying to open %s, assuming USB interface\n",
1856141cc406Sopenharmony_ci		    __func__, device);
1857141cc406Sopenharmony_ci		status = sanei_usb_open(device, &s->fd);
1858141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD) {
1859141cc406Sopenharmony_ci			DBG(6, " ...failed: %s.\n", sane_strstatus(status));
1860141cc406Sopenharmony_ci			cs3_xfree(s);
1861141cc406Sopenharmony_ci			return status;
1862141cc406Sopenharmony_ci		}
1863141cc406Sopenharmony_ci		break;
1864141cc406Sopenharmony_ci	}
1865141cc406Sopenharmony_ci
1866141cc406Sopenharmony_ci	open_devices++;
1867141cc406Sopenharmony_ci	DBG(6, "%s, trying to identify device.\n", __func__);
1868141cc406Sopenharmony_ci
1869141cc406Sopenharmony_ci	/* identify scanner */
1870141cc406Sopenharmony_ci	status = cs3_page_inquiry(s, -1);
1871141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
1872141cc406Sopenharmony_ci		cs3_close(s);
1873141cc406Sopenharmony_ci		return status;
1874141cc406Sopenharmony_ci	}
1875141cc406Sopenharmony_ci
1876141cc406Sopenharmony_ci	strncpy(s->vendor_string, (char *) s->recv_buf + 8, 8);
1877141cc406Sopenharmony_ci	s->vendor_string[8] = '\0';
1878141cc406Sopenharmony_ci	strncpy(s->product_string, (char *) s->recv_buf + 16, 16);
1879141cc406Sopenharmony_ci	s->product_string[16] = '\0';
1880141cc406Sopenharmony_ci	strncpy(s->revision_string, (char *) s->recv_buf + 32, 4);
1881141cc406Sopenharmony_ci	s->revision_string[4] = '\0';
1882141cc406Sopenharmony_ci
1883141cc406Sopenharmony_ci	DBG(10,
1884141cc406Sopenharmony_ci	    "%s, vendor = '%s', product = '%s', revision = '%s'.\n",
1885141cc406Sopenharmony_ci	    __func__, s->vendor_string, s->product_string,
1886141cc406Sopenharmony_ci	    s->revision_string);
1887141cc406Sopenharmony_ci
1888141cc406Sopenharmony_ci	if (!strncmp(s->product_string, "COOLSCANIII     ", 16))
1889141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS30;
1890141cc406Sopenharmony_ci	else if (!strncmp(s->product_string, "LS-40 ED        ", 16))
1891141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS40;
1892141cc406Sopenharmony_ci	else if (!strncmp(s->product_string, "LS-50 ED        ", 16))
1893141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS50;
1894141cc406Sopenharmony_ci	else if (!strncmp(s->product_string, "LS-2000         ", 16))
1895141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS2000;
1896141cc406Sopenharmony_ci	else if (!strncmp(s->product_string, "LS-4000 ED      ", 16))
1897141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS4000;
1898141cc406Sopenharmony_ci	else if (!strncmp(s->product_string, "LS-5000 ED      ", 16))
1899141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS5000;
1900141cc406Sopenharmony_ci	else if (!strncmp(s->product_string, "LS-8000 ED      ", 16))
1901141cc406Sopenharmony_ci		s->type = CS3_TYPE_LS8000;
1902141cc406Sopenharmony_ci
1903141cc406Sopenharmony_ci	if (s->type != CS3_TYPE_UNKOWN)
1904141cc406Sopenharmony_ci		DBG(10,
1905141cc406Sopenharmony_ci		    "%s, device identified as coolscan3 type #%i.\n",
1906141cc406Sopenharmony_ci		    __func__, s->type);
1907141cc406Sopenharmony_ci	else {
1908141cc406Sopenharmony_ci		DBG(10, "%s, device not identified.\n", __func__);
1909141cc406Sopenharmony_ci		cs3_close(s);
1910141cc406Sopenharmony_ci		return SANE_STATUS_UNSUPPORTED;
1911141cc406Sopenharmony_ci	}
1912141cc406Sopenharmony_ci
1913141cc406Sopenharmony_ci	cs3_trim(s->vendor_string);
1914141cc406Sopenharmony_ci	cs3_trim(s->product_string);
1915141cc406Sopenharmony_ci	cs3_trim(s->revision_string);
1916141cc406Sopenharmony_ci
1917141cc406Sopenharmony_ci	if (sp)
1918141cc406Sopenharmony_ci		*sp = s;
1919141cc406Sopenharmony_ci	else {
1920141cc406Sopenharmony_ci		device_list_new =
1921141cc406Sopenharmony_ci			(SANE_Device **) cs3_xrealloc(device_list,
1922141cc406Sopenharmony_ci						      (n_device_list +
1923141cc406Sopenharmony_ci						       2) *
1924141cc406Sopenharmony_ci						      sizeof(SANE_Device *));
1925141cc406Sopenharmony_ci		if (!device_list_new)
1926141cc406Sopenharmony_ci			return SANE_STATUS_NO_MEM;
1927141cc406Sopenharmony_ci		device_list = device_list_new;
1928141cc406Sopenharmony_ci		device_list[n_device_list] =
1929141cc406Sopenharmony_ci			(SANE_Device *) cs3_xmalloc(sizeof(SANE_Device));
1930141cc406Sopenharmony_ci		if (!device_list[n_device_list])
1931141cc406Sopenharmony_ci			return SANE_STATUS_NO_MEM;
1932141cc406Sopenharmony_ci		switch (interface) {
1933141cc406Sopenharmony_ci		case CS3_INTERFACE_UNKNOWN:
1934141cc406Sopenharmony_ci			DBG(1, "BUG: cs3_open(): unknown interface.\n");
1935141cc406Sopenharmony_ci			cs3_close(s);
1936141cc406Sopenharmony_ci			return SANE_STATUS_UNSUPPORTED;
1937141cc406Sopenharmony_ci			break;
1938141cc406Sopenharmony_ci		case CS3_INTERFACE_SCSI:
1939141cc406Sopenharmony_ci			prefix = "scsi:";
1940141cc406Sopenharmony_ci			break;
1941141cc406Sopenharmony_ci		case CS3_INTERFACE_USB:
1942141cc406Sopenharmony_ci			prefix = "usb:";
1943141cc406Sopenharmony_ci			break;
1944141cc406Sopenharmony_ci		}
1945141cc406Sopenharmony_ci
1946141cc406Sopenharmony_ci		line = (char *) cs3_xmalloc(strlen(device) + strlen(prefix) +
1947141cc406Sopenharmony_ci					    1);
1948141cc406Sopenharmony_ci		if (!line)
1949141cc406Sopenharmony_ci			alloc_failed = 1;
1950141cc406Sopenharmony_ci		else {
1951141cc406Sopenharmony_ci			strcpy(line, prefix);
1952141cc406Sopenharmony_ci			strcat(line, device);
1953141cc406Sopenharmony_ci			device_list[n_device_list]->name = line;
1954141cc406Sopenharmony_ci		}
1955141cc406Sopenharmony_ci
1956141cc406Sopenharmony_ci		line = (char *) cs3_xmalloc(strlen(s->vendor_string) + 1);
1957141cc406Sopenharmony_ci		if (!line)
1958141cc406Sopenharmony_ci			alloc_failed = 1;
1959141cc406Sopenharmony_ci		else {
1960141cc406Sopenharmony_ci			strcpy(line, s->vendor_string);
1961141cc406Sopenharmony_ci			device_list[n_device_list]->vendor = line;
1962141cc406Sopenharmony_ci		}
1963141cc406Sopenharmony_ci
1964141cc406Sopenharmony_ci		line = (char *) cs3_xmalloc(strlen(s->product_string) + 1);
1965141cc406Sopenharmony_ci		if (!line)
1966141cc406Sopenharmony_ci			alloc_failed = 1;
1967141cc406Sopenharmony_ci		else {
1968141cc406Sopenharmony_ci			strcpy(line, s->product_string);
1969141cc406Sopenharmony_ci			device_list[n_device_list]->model = line;
1970141cc406Sopenharmony_ci		}
1971141cc406Sopenharmony_ci
1972141cc406Sopenharmony_ci		device_list[n_device_list]->type = "film scanner";
1973141cc406Sopenharmony_ci
1974141cc406Sopenharmony_ci		if (alloc_failed) {
1975141cc406Sopenharmony_ci			cs3_xfree((void *)device_list[n_device_list]->name);
1976141cc406Sopenharmony_ci			cs3_xfree((void *)device_list[n_device_list]->vendor);
1977141cc406Sopenharmony_ci			cs3_xfree((void *)device_list[n_device_list]->model);
1978141cc406Sopenharmony_ci			cs3_xfree(device_list[n_device_list]);
1979141cc406Sopenharmony_ci		} else
1980141cc406Sopenharmony_ci			n_device_list++;
1981141cc406Sopenharmony_ci		device_list[n_device_list] = NULL;
1982141cc406Sopenharmony_ci
1983141cc406Sopenharmony_ci		cs3_close(s);
1984141cc406Sopenharmony_ci	}
1985141cc406Sopenharmony_ci
1986141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1987141cc406Sopenharmony_ci}
1988141cc406Sopenharmony_ci
1989141cc406Sopenharmony_civoid
1990141cc406Sopenharmony_cics3_close(cs3_t * s)
1991141cc406Sopenharmony_ci{
1992141cc406Sopenharmony_ci	cs3_xfree(s->lut_r);
1993141cc406Sopenharmony_ci	cs3_xfree(s->lut_g);
1994141cc406Sopenharmony_ci	cs3_xfree(s->lut_b);
1995141cc406Sopenharmony_ci	cs3_xfree(s->lut_neutral);
1996141cc406Sopenharmony_ci	cs3_xfree(s->line_buf);
1997141cc406Sopenharmony_ci
1998141cc406Sopenharmony_ci	switch (s->interface) {
1999141cc406Sopenharmony_ci	case CS3_INTERFACE_UNKNOWN:
2000141cc406Sopenharmony_ci		DBG(0, "BUG: %s: Unknown interface number.\n", __func__);
2001141cc406Sopenharmony_ci		break;
2002141cc406Sopenharmony_ci	case CS3_INTERFACE_SCSI:
2003141cc406Sopenharmony_ci		sanei_scsi_close(s->fd);
2004141cc406Sopenharmony_ci		open_devices--;
2005141cc406Sopenharmony_ci		break;
2006141cc406Sopenharmony_ci	case CS3_INTERFACE_USB:
2007141cc406Sopenharmony_ci		sanei_usb_close(s->fd);
2008141cc406Sopenharmony_ci		open_devices--;
2009141cc406Sopenharmony_ci		break;
2010141cc406Sopenharmony_ci	}
2011141cc406Sopenharmony_ci
2012141cc406Sopenharmony_ci	cs3_xfree(s);
2013141cc406Sopenharmony_ci}
2014141cc406Sopenharmony_ci
2015141cc406Sopenharmony_cistatic SANE_Status
2016141cc406Sopenharmony_cics3_attach(const char *dev)
2017141cc406Sopenharmony_ci{
2018141cc406Sopenharmony_ci	SANE_Status status;
2019141cc406Sopenharmony_ci
2020141cc406Sopenharmony_ci	if (try_interface == CS3_INTERFACE_UNKNOWN)
2021141cc406Sopenharmony_ci		return SANE_STATUS_UNSUPPORTED;
2022141cc406Sopenharmony_ci
2023141cc406Sopenharmony_ci	status = cs3_open(dev, try_interface, NULL);
2024141cc406Sopenharmony_ci	return status;
2025141cc406Sopenharmony_ci}
2026141cc406Sopenharmony_ci
2027141cc406Sopenharmony_cistatic SANE_Status
2028141cc406Sopenharmony_cics3_scsi_sense_handler(int fd, u_char * sense_buffer, void *arg)
2029141cc406Sopenharmony_ci{
2030141cc406Sopenharmony_ci	cs3_t *s = (cs3_t *) arg;
2031141cc406Sopenharmony_ci
2032141cc406Sopenharmony_ci	(void) fd;		/* to shut up compiler */
2033141cc406Sopenharmony_ci
2034141cc406Sopenharmony_ci	/* sort this out ! XXX */
2035141cc406Sopenharmony_ci
2036141cc406Sopenharmony_ci	s->sense_key = sense_buffer[2] & 0x0f;
2037141cc406Sopenharmony_ci	s->sense_asc = sense_buffer[12];
2038141cc406Sopenharmony_ci	s->sense_ascq = sense_buffer[13];
2039141cc406Sopenharmony_ci	s->sense_info = sense_buffer[3];
2040141cc406Sopenharmony_ci
2041141cc406Sopenharmony_ci	return cs3_parse_sense_data(s);
2042141cc406Sopenharmony_ci}
2043141cc406Sopenharmony_ci
2044141cc406Sopenharmony_cistatic SANE_Status
2045141cc406Sopenharmony_cics3_parse_sense_data(cs3_t * s)
2046141cc406Sopenharmony_ci{
2047141cc406Sopenharmony_ci	SANE_Status status = SANE_STATUS_GOOD;
2048141cc406Sopenharmony_ci
2049141cc406Sopenharmony_ci	s->sense_code =
2050141cc406Sopenharmony_ci		(s->sense_key << 24) + (s->sense_asc << 16) +
2051141cc406Sopenharmony_ci		(s->sense_ascq << 8) + s->sense_info;
2052141cc406Sopenharmony_ci
2053141cc406Sopenharmony_ci	if (s->sense_key)
2054141cc406Sopenharmony_ci		DBG(14, "sense code: %02lx-%02lx-%02lx-%02lx\n", s->sense_key,
2055141cc406Sopenharmony_ci		    s->sense_asc, s->sense_ascq, s->sense_info);
2056141cc406Sopenharmony_ci
2057141cc406Sopenharmony_ci	switch (s->sense_key) {
2058141cc406Sopenharmony_ci	case 0x00:
2059141cc406Sopenharmony_ci		s->status = CS3_STATUS_READY;
2060141cc406Sopenharmony_ci		break;
2061141cc406Sopenharmony_ci
2062141cc406Sopenharmony_ci	case 0x02:
2063141cc406Sopenharmony_ci		switch (s->sense_asc) {
2064141cc406Sopenharmony_ci		case 0x04:
2065141cc406Sopenharmony_ci			DBG(15, " processing\n");
2066141cc406Sopenharmony_ci			s->status = CS3_STATUS_PROCESSING;
2067141cc406Sopenharmony_ci			break;
2068141cc406Sopenharmony_ci		case 0x3a:
2069141cc406Sopenharmony_ci			DBG(15, " no docs\n");
2070141cc406Sopenharmony_ci			s->status = CS3_STATUS_NO_DOCS;
2071141cc406Sopenharmony_ci			break;
2072141cc406Sopenharmony_ci		default:
2073141cc406Sopenharmony_ci			DBG(15, " default\n");
2074141cc406Sopenharmony_ci			s->status = CS3_STATUS_ERROR;
2075141cc406Sopenharmony_ci			status = SANE_STATUS_IO_ERROR;
2076141cc406Sopenharmony_ci			break;
2077141cc406Sopenharmony_ci		}
2078141cc406Sopenharmony_ci		break;
2079141cc406Sopenharmony_ci
2080141cc406Sopenharmony_ci	case 0x09:
2081141cc406Sopenharmony_ci		if ((s->sense_code == 0x09800600)
2082141cc406Sopenharmony_ci		    || (s->sense_code == 0x09800601))
2083141cc406Sopenharmony_ci			s->status = CS3_STATUS_REISSUE;
2084141cc406Sopenharmony_ci		break;
2085141cc406Sopenharmony_ci
2086141cc406Sopenharmony_ci	default:
2087141cc406Sopenharmony_ci		s->status = CS3_STATUS_ERROR;
2088141cc406Sopenharmony_ci		status = SANE_STATUS_IO_ERROR;
2089141cc406Sopenharmony_ci		break;
2090141cc406Sopenharmony_ci	}
2091141cc406Sopenharmony_ci
2092141cc406Sopenharmony_ci	return status;
2093141cc406Sopenharmony_ci}
2094141cc406Sopenharmony_ci
2095141cc406Sopenharmony_cistatic void
2096141cc406Sopenharmony_cics3_init_buffer(cs3_t * s)
2097141cc406Sopenharmony_ci{
2098141cc406Sopenharmony_ci	s->n_cmd = 0;
2099141cc406Sopenharmony_ci	s->n_send = 0;
2100141cc406Sopenharmony_ci	s->n_recv = 0;
2101141cc406Sopenharmony_ci}
2102141cc406Sopenharmony_ci
2103141cc406Sopenharmony_cistatic SANE_Status
2104141cc406Sopenharmony_cics3_pack_byte(cs3_t * s, SANE_Byte byte)
2105141cc406Sopenharmony_ci{
2106141cc406Sopenharmony_ci	while (s->send_buf_size <= s->n_send) {
2107141cc406Sopenharmony_ci		s->send_buf_size += 16;
2108141cc406Sopenharmony_ci		s->send_buf =
2109141cc406Sopenharmony_ci			(SANE_Byte *) cs3_xrealloc(s->send_buf,
2110141cc406Sopenharmony_ci						   s->send_buf_size);
2111141cc406Sopenharmony_ci		if (!s->send_buf)
2112141cc406Sopenharmony_ci			return SANE_STATUS_NO_MEM;
2113141cc406Sopenharmony_ci	}
2114141cc406Sopenharmony_ci
2115141cc406Sopenharmony_ci	s->send_buf[s->n_send++] = byte;
2116141cc406Sopenharmony_ci
2117141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2118141cc406Sopenharmony_ci}
2119141cc406Sopenharmony_ci
2120141cc406Sopenharmony_cistatic void
2121141cc406Sopenharmony_cics3_pack_long(cs3_t * s, unsigned long val)
2122141cc406Sopenharmony_ci{
2123141cc406Sopenharmony_ci	cs3_pack_byte(s, (val >> 24) & 0xff);
2124141cc406Sopenharmony_ci	cs3_pack_byte(s, (val >> 16) & 0xff);
2125141cc406Sopenharmony_ci	cs3_pack_byte(s, (val >> 8) & 0xff);
2126141cc406Sopenharmony_ci	cs3_pack_byte(s, val & 0xff);
2127141cc406Sopenharmony_ci}
2128141cc406Sopenharmony_ci
2129141cc406Sopenharmony_cistatic void
2130141cc406Sopenharmony_cics3_pack_word(cs3_t * s, unsigned long val)
2131141cc406Sopenharmony_ci{
2132141cc406Sopenharmony_ci	cs3_pack_byte(s, (val >> 8) & 0xff);
2133141cc406Sopenharmony_ci	cs3_pack_byte(s, val & 0xff);
2134141cc406Sopenharmony_ci}
2135141cc406Sopenharmony_ci
2136141cc406Sopenharmony_cistatic SANE_Status
2137141cc406Sopenharmony_cics3_parse_cmd(cs3_t * s, char *text)
2138141cc406Sopenharmony_ci{
2139141cc406Sopenharmony_ci	size_t i, j;
2140141cc406Sopenharmony_ci	char c, h;
2141141cc406Sopenharmony_ci	SANE_Status status;
2142141cc406Sopenharmony_ci
2143141cc406Sopenharmony_ci	for (i = 0; i < strlen(text); i += 2)
2144141cc406Sopenharmony_ci		if (text[i] == ' ')
2145141cc406Sopenharmony_ci			i--;	/* a bit dirty... advance by -1+2=1 */
2146141cc406Sopenharmony_ci		else {
2147141cc406Sopenharmony_ci			if ((!isxdigit(text[i])) || (!isxdigit(text[i + 1])))
2148141cc406Sopenharmony_ci				DBG(1,
2149141cc406Sopenharmony_ci				    "BUG: cs3_parse_cmd(): Parser got invalid character.\n");
2150141cc406Sopenharmony_ci			c = 0;
2151141cc406Sopenharmony_ci			for (j = 0; j < 2; j++) {
2152141cc406Sopenharmony_ci				h = tolower(text[i + j]);
2153141cc406Sopenharmony_ci				if ((h >= 'a') && (h <= 'f'))
2154141cc406Sopenharmony_ci					c += 10 + h - 'a';
2155141cc406Sopenharmony_ci				else
2156141cc406Sopenharmony_ci					c += h - '0';
2157141cc406Sopenharmony_ci				if (j == 0)
2158141cc406Sopenharmony_ci					c <<= 4;
2159141cc406Sopenharmony_ci			}
2160141cc406Sopenharmony_ci			status = cs3_pack_byte(s, c);
2161141cc406Sopenharmony_ci			if (status != SANE_STATUS_GOOD)
2162141cc406Sopenharmony_ci				return status;
2163141cc406Sopenharmony_ci		}
2164141cc406Sopenharmony_ci
2165141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2166141cc406Sopenharmony_ci}
2167141cc406Sopenharmony_ci
2168141cc406Sopenharmony_cistatic SANE_Status
2169141cc406Sopenharmony_cics3_grow_send_buffer(cs3_t * s)
2170141cc406Sopenharmony_ci{
2171141cc406Sopenharmony_ci	if (s->n_send > s->send_buf_size) {
2172141cc406Sopenharmony_ci		s->send_buf_size = s->n_send;
2173141cc406Sopenharmony_ci		s->send_buf =
2174141cc406Sopenharmony_ci			(SANE_Byte *) cs3_xrealloc(s->send_buf,
2175141cc406Sopenharmony_ci						   s->send_buf_size);
2176141cc406Sopenharmony_ci		if (!s->send_buf)
2177141cc406Sopenharmony_ci			return SANE_STATUS_NO_MEM;
2178141cc406Sopenharmony_ci	}
2179141cc406Sopenharmony_ci
2180141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2181141cc406Sopenharmony_ci}
2182141cc406Sopenharmony_ci
2183141cc406Sopenharmony_cistatic SANE_Status
2184141cc406Sopenharmony_cics3_issue_cmd(cs3_t * s)
2185141cc406Sopenharmony_ci{
2186141cc406Sopenharmony_ci	SANE_Status status = SANE_STATUS_INVAL;
2187141cc406Sopenharmony_ci	size_t n_data, n_status;
2188141cc406Sopenharmony_ci	static SANE_Byte status_buf[8];
2189141cc406Sopenharmony_ci	int status_only = 0;
2190141cc406Sopenharmony_ci
2191141cc406Sopenharmony_ci	DBG(20,
2192141cc406Sopenharmony_ci	    "cs3_issue_cmd(): opcode = 0x%02x, n_send = %lu, n_recv = %lu.\n",
2193141cc406Sopenharmony_ci	    s->send_buf[0], (unsigned long) s->n_send,
2194141cc406Sopenharmony_ci	    (unsigned long) s->n_recv);
2195141cc406Sopenharmony_ci
2196141cc406Sopenharmony_ci	s->status = CS3_STATUS_READY;
2197141cc406Sopenharmony_ci
2198141cc406Sopenharmony_ci	if (!s->n_cmd)
2199141cc406Sopenharmony_ci		switch (s->send_buf[0]) {
2200141cc406Sopenharmony_ci		case 0x00:
2201141cc406Sopenharmony_ci		case 0x12:
2202141cc406Sopenharmony_ci		case 0x15:
2203141cc406Sopenharmony_ci		case 0x16:
2204141cc406Sopenharmony_ci		case 0x17:
2205141cc406Sopenharmony_ci		case 0x1a:
2206141cc406Sopenharmony_ci		case 0x1b:
2207141cc406Sopenharmony_ci		case 0x1c:
2208141cc406Sopenharmony_ci		case 0x1d:
2209141cc406Sopenharmony_ci		case 0xc0:
2210141cc406Sopenharmony_ci		case 0xc1:
2211141cc406Sopenharmony_ci			s->n_cmd = 6;
2212141cc406Sopenharmony_ci			break;
2213141cc406Sopenharmony_ci		case 0x24:
2214141cc406Sopenharmony_ci		case 0x25:
2215141cc406Sopenharmony_ci		case 0x28:
2216141cc406Sopenharmony_ci		case 0x2a:
2217141cc406Sopenharmony_ci		case 0xe0:
2218141cc406Sopenharmony_ci		case 0xe1:
2219141cc406Sopenharmony_ci			s->n_cmd = 10;
2220141cc406Sopenharmony_ci			break;
2221141cc406Sopenharmony_ci		default:
2222141cc406Sopenharmony_ci			DBG(1,
2223141cc406Sopenharmony_ci			    "BUG: cs3_issue_cmd(): Unknown command opcode 0x%02x.\n",
2224141cc406Sopenharmony_ci			    s->send_buf[0]);
2225141cc406Sopenharmony_ci			break;
2226141cc406Sopenharmony_ci		}
2227141cc406Sopenharmony_ci
2228141cc406Sopenharmony_ci	if (s->n_send < s->n_cmd) {
2229141cc406Sopenharmony_ci		DBG(1,
2230141cc406Sopenharmony_ci		    "BUG: cs3_issue_cmd(): Negative number of data out bytes requested.\n");
2231141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
2232141cc406Sopenharmony_ci	}
2233141cc406Sopenharmony_ci
2234141cc406Sopenharmony_ci	n_data = s->n_send - s->n_cmd;
2235141cc406Sopenharmony_ci	if (s->n_recv > 0) {
2236141cc406Sopenharmony_ci		if (n_data > 0) {
2237141cc406Sopenharmony_ci			DBG(1,
2238141cc406Sopenharmony_ci			    "BUG: cs3_issue_cmd(): Both data in and data out requested.\n");
2239141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
2240141cc406Sopenharmony_ci		} else {
2241141cc406Sopenharmony_ci			n_data = s->n_recv;
2242141cc406Sopenharmony_ci		}
2243141cc406Sopenharmony_ci	}
2244141cc406Sopenharmony_ci
2245141cc406Sopenharmony_ci	s->recv_buf = (SANE_Byte *) cs3_xrealloc(s->recv_buf, s->n_recv);
2246141cc406Sopenharmony_ci	if (!s->recv_buf)
2247141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
2248141cc406Sopenharmony_ci
2249141cc406Sopenharmony_ci	switch (s->interface) {
2250141cc406Sopenharmony_ci	case CS3_INTERFACE_UNKNOWN:
2251141cc406Sopenharmony_ci		DBG(1,
2252141cc406Sopenharmony_ci		    "BUG: cs3_issue_cmd(): Unknown or uninitialized interface number.\n");
2253141cc406Sopenharmony_ci		break;
2254141cc406Sopenharmony_ci
2255141cc406Sopenharmony_ci	case CS3_INTERFACE_SCSI:
2256141cc406Sopenharmony_ci		sanei_scsi_cmd2(s->fd, s->send_buf, s->n_cmd,
2257141cc406Sopenharmony_ci				s->send_buf + s->n_cmd, s->n_send - s->n_cmd,
2258141cc406Sopenharmony_ci				s->recv_buf, &s->n_recv);
2259141cc406Sopenharmony_ci		status = SANE_STATUS_GOOD;
2260141cc406Sopenharmony_ci		break;
2261141cc406Sopenharmony_ci
2262141cc406Sopenharmony_ci	case CS3_INTERFACE_USB:
2263141cc406Sopenharmony_ci		status = sanei_usb_write_bulk(s->fd, s->send_buf, &s->n_cmd);
2264141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD) {
2265141cc406Sopenharmony_ci			DBG(1,
2266141cc406Sopenharmony_ci			    "Error: cs3_issue_cmd(): Could not write command.\n");
2267141cc406Sopenharmony_ci			return SANE_STATUS_IO_ERROR;
2268141cc406Sopenharmony_ci		}
2269141cc406Sopenharmony_ci
2270141cc406Sopenharmony_ci		switch (cs3_phase_check(s)) {
2271141cc406Sopenharmony_ci		case CS3_PHASE_OUT:
2272141cc406Sopenharmony_ci			if (s->n_send - s->n_cmd < n_data || !n_data) {
2273141cc406Sopenharmony_ci				DBG(4,
2274141cc406Sopenharmony_ci				    "Error: cs3_issue_cmd(): Unexpected data out phase.\n");
2275141cc406Sopenharmony_ci				return SANE_STATUS_IO_ERROR;
2276141cc406Sopenharmony_ci			}
2277141cc406Sopenharmony_ci			status = sanei_usb_write_bulk(s->fd,
2278141cc406Sopenharmony_ci						      s->send_buf + s->n_cmd,
2279141cc406Sopenharmony_ci						      &n_data);
2280141cc406Sopenharmony_ci			break;
2281141cc406Sopenharmony_ci
2282141cc406Sopenharmony_ci		case CS3_PHASE_IN:
2283141cc406Sopenharmony_ci			if (s->n_recv < n_data || !n_data) {
2284141cc406Sopenharmony_ci				DBG(4,
2285141cc406Sopenharmony_ci				    "Error: cs3_issue_cmd(): Unexpected data in phase.\n");
2286141cc406Sopenharmony_ci				return SANE_STATUS_IO_ERROR;
2287141cc406Sopenharmony_ci			}
2288141cc406Sopenharmony_ci			status = sanei_usb_read_bulk(s->fd, s->recv_buf,
2289141cc406Sopenharmony_ci						     &n_data);
2290141cc406Sopenharmony_ci			s->n_recv = n_data;
2291141cc406Sopenharmony_ci			break;
2292141cc406Sopenharmony_ci
2293141cc406Sopenharmony_ci		case CS3_PHASE_NONE:
2294141cc406Sopenharmony_ci			DBG(4, "%s: No command received!\n", __func__);
2295141cc406Sopenharmony_ci			return SANE_STATUS_IO_ERROR;
2296141cc406Sopenharmony_ci
2297141cc406Sopenharmony_ci		default:
2298141cc406Sopenharmony_ci			if (n_data) {
2299141cc406Sopenharmony_ci				DBG(4,
2300141cc406Sopenharmony_ci				    "%s: Unexpected non-data phase, but n_data != 0 (%lu).\n",
2301141cc406Sopenharmony_ci				    __func__, (u_long) n_data);
2302141cc406Sopenharmony_ci				status_only = 1;
2303141cc406Sopenharmony_ci			}
2304141cc406Sopenharmony_ci			break;
2305141cc406Sopenharmony_ci		}
2306141cc406Sopenharmony_ci
2307141cc406Sopenharmony_ci		n_status = 8;
2308141cc406Sopenharmony_ci		status = sanei_usb_read_bulk(s->fd, status_buf, &n_status);
2309141cc406Sopenharmony_ci		if (n_status != 8) {
2310141cc406Sopenharmony_ci			DBG(4,
2311141cc406Sopenharmony_ci			    "Error: cs3_issue_cmd(): Failed to read 8 status bytes from USB.\n");
2312141cc406Sopenharmony_ci			return SANE_STATUS_IO_ERROR;
2313141cc406Sopenharmony_ci		}
2314141cc406Sopenharmony_ci
2315141cc406Sopenharmony_ci		s->sense_key = status_buf[1] & 0x0f;
2316141cc406Sopenharmony_ci		s->sense_asc = status_buf[2] & 0xff;
2317141cc406Sopenharmony_ci		s->sense_ascq = status_buf[3] & 0xff;
2318141cc406Sopenharmony_ci		s->sense_info = status_buf[4] & 0xff;
2319141cc406Sopenharmony_ci		status = cs3_parse_sense_data(s);
2320141cc406Sopenharmony_ci		break;
2321141cc406Sopenharmony_ci	}
2322141cc406Sopenharmony_ci
2323141cc406Sopenharmony_ci	if (status_only)
2324141cc406Sopenharmony_ci		return SANE_STATUS_IO_ERROR;
2325141cc406Sopenharmony_ci	else
2326141cc406Sopenharmony_ci		return status;
2327141cc406Sopenharmony_ci}
2328141cc406Sopenharmony_ci
2329141cc406Sopenharmony_cistatic cs3_phase_t
2330141cc406Sopenharmony_cics3_phase_check(cs3_t * s)
2331141cc406Sopenharmony_ci{
2332141cc406Sopenharmony_ci	static SANE_Byte phase_send_buf[1] = { 0xd0 }, phase_recv_buf[1];
2333141cc406Sopenharmony_ci	SANE_Status status = 0;
2334141cc406Sopenharmony_ci	size_t n = 1;
2335141cc406Sopenharmony_ci
2336141cc406Sopenharmony_ci	status = sanei_usb_write_bulk(s->fd, phase_send_buf, &n);
2337141cc406Sopenharmony_ci	status |= sanei_usb_read_bulk(s->fd, phase_recv_buf, &n);
2338141cc406Sopenharmony_ci
2339141cc406Sopenharmony_ci	DBG(40, "%s: returned phase = 0x%02x.\n", __func__,
2340141cc406Sopenharmony_ci	    phase_recv_buf[0]);
2341141cc406Sopenharmony_ci
2342141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2343141cc406Sopenharmony_ci		return -1;
2344141cc406Sopenharmony_ci	else
2345141cc406Sopenharmony_ci		return phase_recv_buf[0];
2346141cc406Sopenharmony_ci}
2347141cc406Sopenharmony_ci
2348141cc406Sopenharmony_cistatic SANE_Status
2349141cc406Sopenharmony_cics3_scanner_ready(cs3_t * s, int flags)
2350141cc406Sopenharmony_ci{
2351141cc406Sopenharmony_ci	SANE_Status status = SANE_STATUS_GOOD;
2352141cc406Sopenharmony_ci	int i = -1;
2353141cc406Sopenharmony_ci	unsigned long count = 0;
2354141cc406Sopenharmony_ci	int retry = 3;
2355141cc406Sopenharmony_ci
2356141cc406Sopenharmony_ci	do {
2357141cc406Sopenharmony_ci		if (i >= 0)	/* dirty !!! */
2358141cc406Sopenharmony_ci			usleep(1000000);
2359141cc406Sopenharmony_ci		/* test unit ready */
2360141cc406Sopenharmony_ci		cs3_init_buffer(s);
2361141cc406Sopenharmony_ci		for (i = 0; i < 6; i++)
2362141cc406Sopenharmony_ci			cs3_pack_byte(s, 0x00);
2363141cc406Sopenharmony_ci
2364141cc406Sopenharmony_ci		status = cs3_issue_cmd(s);
2365141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
2366141cc406Sopenharmony_ci			if (--retry < 0)
2367141cc406Sopenharmony_ci				return status;
2368141cc406Sopenharmony_ci
2369141cc406Sopenharmony_ci		if (++count > 120) {	/* 120s timeout */
2370141cc406Sopenharmony_ci			DBG(4, "Error: %s: Timeout expired.\n", __func__);
2371141cc406Sopenharmony_ci			status = SANE_STATUS_IO_ERROR;
2372141cc406Sopenharmony_ci			break;
2373141cc406Sopenharmony_ci		}
2374141cc406Sopenharmony_ci	}
2375141cc406Sopenharmony_ci	while (s->status & ~flags);	/* until all relevant bits are 0 */
2376141cc406Sopenharmony_ci
2377141cc406Sopenharmony_ci	return status;
2378141cc406Sopenharmony_ci}
2379141cc406Sopenharmony_ci
2380141cc406Sopenharmony_cistatic SANE_Status
2381141cc406Sopenharmony_cics3_page_inquiry(cs3_t * s, int page)
2382141cc406Sopenharmony_ci{
2383141cc406Sopenharmony_ci	SANE_Status status;
2384141cc406Sopenharmony_ci
2385141cc406Sopenharmony_ci	size_t n;
2386141cc406Sopenharmony_ci
2387141cc406Sopenharmony_ci	if (page >= 0) {
2388141cc406Sopenharmony_ci
2389141cc406Sopenharmony_ci		cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2390141cc406Sopenharmony_ci		cs3_init_buffer(s);
2391141cc406Sopenharmony_ci		cs3_parse_cmd(s, "12 01");
2392141cc406Sopenharmony_ci		cs3_pack_byte(s, page);
2393141cc406Sopenharmony_ci		cs3_parse_cmd(s, "00 04 00");
2394141cc406Sopenharmony_ci		s->n_recv = 4;
2395141cc406Sopenharmony_ci		status = cs3_issue_cmd(s);
2396141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD) {
2397141cc406Sopenharmony_ci			DBG(4,
2398141cc406Sopenharmony_ci			    "Error: cs3_page_inquiry(): Inquiry of page size failed: %s.\n",
2399141cc406Sopenharmony_ci			    sane_strstatus(status));
2400141cc406Sopenharmony_ci			return status;
2401141cc406Sopenharmony_ci		}
2402141cc406Sopenharmony_ci
2403141cc406Sopenharmony_ci		n = s->recv_buf[3] + 4;
2404141cc406Sopenharmony_ci
2405141cc406Sopenharmony_ci	} else
2406141cc406Sopenharmony_ci		n = 36;
2407141cc406Sopenharmony_ci
2408141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2409141cc406Sopenharmony_ci	cs3_init_buffer(s);
2410141cc406Sopenharmony_ci	if (page >= 0) {
2411141cc406Sopenharmony_ci		cs3_parse_cmd(s, "12 01");
2412141cc406Sopenharmony_ci		cs3_pack_byte(s, page);
2413141cc406Sopenharmony_ci		cs3_parse_cmd(s, "00");
2414141cc406Sopenharmony_ci	} else
2415141cc406Sopenharmony_ci		cs3_parse_cmd(s, "12 00 00 00");
2416141cc406Sopenharmony_ci	cs3_pack_byte(s, n);
2417141cc406Sopenharmony_ci	cs3_parse_cmd(s, "00");
2418141cc406Sopenharmony_ci	s->n_recv = n;
2419141cc406Sopenharmony_ci
2420141cc406Sopenharmony_ci	status = cs3_issue_cmd(s);
2421141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
2422141cc406Sopenharmony_ci		DBG(4, "Error: %s: inquiry of page failed: %s.\n",
2423141cc406Sopenharmony_ci		    __func__, sane_strstatus(status));
2424141cc406Sopenharmony_ci		return status;
2425141cc406Sopenharmony_ci	}
2426141cc406Sopenharmony_ci
2427141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2428141cc406Sopenharmony_ci}
2429141cc406Sopenharmony_ci
2430141cc406Sopenharmony_cistatic SANE_Status
2431141cc406Sopenharmony_cics3_full_inquiry(cs3_t * s)
2432141cc406Sopenharmony_ci{
2433141cc406Sopenharmony_ci	SANE_Status status;
2434141cc406Sopenharmony_ci	int pitch, pitch_max;
2435141cc406Sopenharmony_ci	cs3_pixel_t pixel;
2436141cc406Sopenharmony_ci
2437141cc406Sopenharmony_ci	DBG(4, "%s\n", __func__);
2438141cc406Sopenharmony_ci
2439141cc406Sopenharmony_ci	status = cs3_page_inquiry(s, 0xc1);
2440141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2441141cc406Sopenharmony_ci		return status;
2442141cc406Sopenharmony_ci
2443141cc406Sopenharmony_ci	s->maxbits = s->recv_buf[82];
2444141cc406Sopenharmony_ci	if (s->type == CS3_TYPE_LS30)	/* must be overridden, LS-30 claims to have 12 bits */
2445141cc406Sopenharmony_ci		s->maxbits = 10;
2446141cc406Sopenharmony_ci
2447141cc406Sopenharmony_ci	s->n_lut = 1;
2448141cc406Sopenharmony_ci	s->n_lut <<= s->maxbits;
2449141cc406Sopenharmony_ci	s->lut_r =
2450141cc406Sopenharmony_ci		(cs3_pixel_t *) cs3_xrealloc(s->lut_r,
2451141cc406Sopenharmony_ci					     s->n_lut * sizeof(cs3_pixel_t));
2452141cc406Sopenharmony_ci	s->lut_g =
2453141cc406Sopenharmony_ci		(cs3_pixel_t *) cs3_xrealloc(s->lut_g,
2454141cc406Sopenharmony_ci					     s->n_lut * sizeof(cs3_pixel_t));
2455141cc406Sopenharmony_ci	s->lut_b =
2456141cc406Sopenharmony_ci		(cs3_pixel_t *) cs3_xrealloc(s->lut_b,
2457141cc406Sopenharmony_ci					     s->n_lut * sizeof(cs3_pixel_t));
2458141cc406Sopenharmony_ci	s->lut_neutral =
2459141cc406Sopenharmony_ci		(cs3_pixel_t *) cs3_xrealloc(s->lut_neutral,
2460141cc406Sopenharmony_ci					     s->n_lut * sizeof(cs3_pixel_t));
2461141cc406Sopenharmony_ci
2462141cc406Sopenharmony_ci	if (!s->lut_r || !s->lut_g || !s->lut_b || !s->lut_neutral) {
2463141cc406Sopenharmony_ci		cs3_xfree(s->lut_r);
2464141cc406Sopenharmony_ci		cs3_xfree(s->lut_g);
2465141cc406Sopenharmony_ci		cs3_xfree(s->lut_b);
2466141cc406Sopenharmony_ci		cs3_xfree(s->lut_neutral);
2467141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
2468141cc406Sopenharmony_ci	}
2469141cc406Sopenharmony_ci
2470141cc406Sopenharmony_ci	for (pixel = 0; pixel < s->n_lut; pixel++) {
2471141cc406Sopenharmony_ci		s->lut_r[pixel] = s->lut_g[pixel] = s->lut_b[pixel] =
2472141cc406Sopenharmony_ci			s->lut_neutral[pixel] = pixel;
2473141cc406Sopenharmony_ci	}
2474141cc406Sopenharmony_ci
2475141cc406Sopenharmony_ci	s->resx_optical = 256 * s->recv_buf[18] + s->recv_buf[19];
2476141cc406Sopenharmony_ci	s->resx_max = 256 * s->recv_buf[20] + s->recv_buf[21];
2477141cc406Sopenharmony_ci	s->resx_min = 256 * s->recv_buf[22] + s->recv_buf[23];
2478141cc406Sopenharmony_ci	s->boundaryx =
2479141cc406Sopenharmony_ci		65536 * (256 * s->recv_buf[36] + s->recv_buf[37]) +
2480141cc406Sopenharmony_ci		256 * s->recv_buf[38] + s->recv_buf[39];
2481141cc406Sopenharmony_ci
2482141cc406Sopenharmony_ci	s->resy_optical = 256 * s->recv_buf[40] + s->recv_buf[41];
2483141cc406Sopenharmony_ci	s->resy_max = 256 * s->recv_buf[42] + s->recv_buf[43];
2484141cc406Sopenharmony_ci	s->resy_min = 256 * s->recv_buf[44] + s->recv_buf[45];
2485141cc406Sopenharmony_ci	s->boundaryy =
2486141cc406Sopenharmony_ci		65536 * (256 * s->recv_buf[58] + s->recv_buf[59]) +
2487141cc406Sopenharmony_ci		256 * s->recv_buf[60] + s->recv_buf[61];
2488141cc406Sopenharmony_ci
2489141cc406Sopenharmony_ci	s->focus_min = 256 * s->recv_buf[76] + s->recv_buf[77];
2490141cc406Sopenharmony_ci	s->focus_max = 256 * s->recv_buf[78] + s->recv_buf[79];
2491141cc406Sopenharmony_ci
2492141cc406Sopenharmony_ci	s->n_frames = s->recv_buf[75];
2493141cc406Sopenharmony_ci
2494141cc406Sopenharmony_ci	s->frame_offset = s->resy_max * 1.5 + 1;	/* works for LS-30, maybe not for others */
2495141cc406Sopenharmony_ci
2496141cc406Sopenharmony_ci	/* generate resolution list for x */
2497141cc406Sopenharmony_ci	s->resx_n_list = pitch_max =
2498141cc406Sopenharmony_ci		floor(s->resx_max / (double) s->resx_min);
2499141cc406Sopenharmony_ci	s->resx_list =
2500141cc406Sopenharmony_ci		(unsigned int *) cs3_xrealloc(s->resx_list,
2501141cc406Sopenharmony_ci					      pitch_max *
2502141cc406Sopenharmony_ci					      sizeof(unsigned int));
2503141cc406Sopenharmony_ci	for (pitch = 1; pitch <= pitch_max; pitch++)
2504141cc406Sopenharmony_ci		s->resx_list[pitch - 1] = s->resx_max / pitch;
2505141cc406Sopenharmony_ci
2506141cc406Sopenharmony_ci	/* generate resolution list for y */
2507141cc406Sopenharmony_ci	s->resy_n_list = pitch_max =
2508141cc406Sopenharmony_ci		floor(s->resy_max / (double) s->resy_min);
2509141cc406Sopenharmony_ci	s->resy_list =
2510141cc406Sopenharmony_ci		(unsigned int *) cs3_xrealloc(s->resy_list,
2511141cc406Sopenharmony_ci					      pitch_max *
2512141cc406Sopenharmony_ci					      sizeof(unsigned int));
2513141cc406Sopenharmony_ci
2514141cc406Sopenharmony_ci	for (pitch = 1; pitch <= pitch_max; pitch++)
2515141cc406Sopenharmony_ci		s->resy_list[pitch - 1] = s->resy_max / pitch;
2516141cc406Sopenharmony_ci
2517141cc406Sopenharmony_ci	s->unit_dpi = s->resx_max;
2518141cc406Sopenharmony_ci	s->unit_mm = 25.4 / s->unit_dpi;
2519141cc406Sopenharmony_ci
2520141cc406Sopenharmony_ci	DBG(4, " maximum depth:	%d\n", s->maxbits);
2521141cc406Sopenharmony_ci	DBG(4, " focus:		%d/%d\n", s->focus_min, s->focus_max);
2522141cc406Sopenharmony_ci	DBG(4, " resolution (x):	%d (%d-%d)\n", s->resx_optical,
2523141cc406Sopenharmony_ci	    s->resx_min, s->resx_max);
2524141cc406Sopenharmony_ci	DBG(4, " resolution (y):	%d (%d-%d)\n", s->resy_optical,
2525141cc406Sopenharmony_ci	    s->resy_min, s->resy_max);
2526141cc406Sopenharmony_ci	DBG(4, " frames:		%d\n", s->n_frames);
2527141cc406Sopenharmony_ci	DBG(4, " frame offset:	%ld\n", s->frame_offset);
2528141cc406Sopenharmony_ci
2529141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2530141cc406Sopenharmony_ci}
2531141cc406Sopenharmony_ci
2532141cc406Sopenharmony_cistatic SANE_Status
2533141cc406Sopenharmony_cics3_execute(cs3_t * s)
2534141cc406Sopenharmony_ci{
2535141cc406Sopenharmony_ci	DBG(16, "%s\n", __func__);
2536141cc406Sopenharmony_ci
2537141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2538141cc406Sopenharmony_ci	cs3_init_buffer(s);
2539141cc406Sopenharmony_ci	cs3_parse_cmd(s, "c1 00 00 00 00 00");
2540141cc406Sopenharmony_ci	return cs3_issue_cmd(s);
2541141cc406Sopenharmony_ci}
2542141cc406Sopenharmony_ci
2543141cc406Sopenharmony_cistatic SANE_Status
2544141cc406Sopenharmony_cics3_issue_and_execute(cs3_t * s)
2545141cc406Sopenharmony_ci{
2546141cc406Sopenharmony_ci	SANE_Status status;
2547141cc406Sopenharmony_ci
2548141cc406Sopenharmony_ci	DBG(10, "%s, opcode = %02x\n", __func__, s->send_buf[0]);
2549141cc406Sopenharmony_ci
2550141cc406Sopenharmony_ci	status = cs3_issue_cmd(s);
2551141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2552141cc406Sopenharmony_ci		return status;
2553141cc406Sopenharmony_ci
2554141cc406Sopenharmony_ci	return cs3_execute(s);
2555141cc406Sopenharmony_ci}
2556141cc406Sopenharmony_ci
2557141cc406Sopenharmony_cistatic SANE_Status
2558141cc406Sopenharmony_cics3_mode_select(cs3_t * s)
2559141cc406Sopenharmony_ci{
2560141cc406Sopenharmony_ci	DBG(4, "%s\n", __func__);
2561141cc406Sopenharmony_ci
2562141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2563141cc406Sopenharmony_ci	cs3_init_buffer(s);
2564141cc406Sopenharmony_ci
2565141cc406Sopenharmony_ci	cs3_parse_cmd(s,
2566141cc406Sopenharmony_ci		      "15 10 00 00 14 00 00 00 00 08 00 00 00 00 00 00 00 01 03 06 00 00");
2567141cc406Sopenharmony_ci	cs3_pack_word(s, s->unit_dpi);
2568141cc406Sopenharmony_ci	cs3_parse_cmd(s, "00 00");
2569141cc406Sopenharmony_ci
2570141cc406Sopenharmony_ci	return cs3_issue_cmd(s);
2571141cc406Sopenharmony_ci}
2572141cc406Sopenharmony_ci
2573141cc406Sopenharmony_cistatic SANE_Status
2574141cc406Sopenharmony_cics3_load(cs3_t * s)
2575141cc406Sopenharmony_ci{
2576141cc406Sopenharmony_ci	SANE_Status status;
2577141cc406Sopenharmony_ci
2578141cc406Sopenharmony_ci	DBG(6, "%s\n", __func__);
2579141cc406Sopenharmony_ci
2580141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2581141cc406Sopenharmony_ci	cs3_init_buffer(s);
2582141cc406Sopenharmony_ci	cs3_parse_cmd(s, "e0 00 d1 00 00 00 00 00 0d 00");
2583141cc406Sopenharmony_ci	s->n_send += 13;
2584141cc406Sopenharmony_ci
2585141cc406Sopenharmony_ci	status = cs3_grow_send_buffer(s);
2586141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2587141cc406Sopenharmony_ci		return status;
2588141cc406Sopenharmony_ci
2589141cc406Sopenharmony_ci	return cs3_issue_and_execute(s);
2590141cc406Sopenharmony_ci}
2591141cc406Sopenharmony_ci
2592141cc406Sopenharmony_cistatic SANE_Status
2593141cc406Sopenharmony_cics3_eject(cs3_t * s)
2594141cc406Sopenharmony_ci{
2595141cc406Sopenharmony_ci	SANE_Status status;
2596141cc406Sopenharmony_ci
2597141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2598141cc406Sopenharmony_ci	cs3_init_buffer(s);
2599141cc406Sopenharmony_ci	cs3_parse_cmd(s, "e0 00 d0 00 00 00 00 00 0d 00");
2600141cc406Sopenharmony_ci	s->n_send += 13;
2601141cc406Sopenharmony_ci
2602141cc406Sopenharmony_ci	status = cs3_grow_send_buffer(s);
2603141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2604141cc406Sopenharmony_ci		return status;
2605141cc406Sopenharmony_ci
2606141cc406Sopenharmony_ci	return cs3_issue_and_execute(s);
2607141cc406Sopenharmony_ci}
2608141cc406Sopenharmony_ci
2609141cc406Sopenharmony_cistatic SANE_Status
2610141cc406Sopenharmony_cics3_reset(cs3_t * s)
2611141cc406Sopenharmony_ci{
2612141cc406Sopenharmony_ci	SANE_Status status;
2613141cc406Sopenharmony_ci
2614141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2615141cc406Sopenharmony_ci	cs3_init_buffer(s);
2616141cc406Sopenharmony_ci	cs3_parse_cmd(s, "e0 00 80 00 00 00 00 00 0d 00");
2617141cc406Sopenharmony_ci	s->n_send += 13;
2618141cc406Sopenharmony_ci
2619141cc406Sopenharmony_ci	status = cs3_grow_send_buffer(s);
2620141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2621141cc406Sopenharmony_ci		return status;
2622141cc406Sopenharmony_ci
2623141cc406Sopenharmony_ci	return cs3_issue_and_execute(s);
2624141cc406Sopenharmony_ci}
2625141cc406Sopenharmony_ci
2626141cc406Sopenharmony_ci
2627141cc406Sopenharmony_cistatic SANE_Status
2628141cc406Sopenharmony_cics3_reserve_unit(cs3_t * s)
2629141cc406Sopenharmony_ci{
2630141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
2631141cc406Sopenharmony_ci
2632141cc406Sopenharmony_ci	cs3_init_buffer(s);
2633141cc406Sopenharmony_ci	cs3_parse_cmd(s, "16 00 00 00 00 00");
2634141cc406Sopenharmony_ci	return cs3_issue_cmd(s);
2635141cc406Sopenharmony_ci}
2636141cc406Sopenharmony_ci
2637141cc406Sopenharmony_cistatic SANE_Status
2638141cc406Sopenharmony_cics3_release_unit(cs3_t * s)
2639141cc406Sopenharmony_ci{
2640141cc406Sopenharmony_ci	DBG(10, "%s\n", __func__);
2641141cc406Sopenharmony_ci
2642141cc406Sopenharmony_ci	cs3_init_buffer(s);
2643141cc406Sopenharmony_ci	cs3_parse_cmd(s, "17 00 00 00 00 00");
2644141cc406Sopenharmony_ci	return cs3_issue_cmd(s);
2645141cc406Sopenharmony_ci}
2646141cc406Sopenharmony_ci
2647141cc406Sopenharmony_ci
2648141cc406Sopenharmony_cistatic SANE_Status
2649141cc406Sopenharmony_cics3_set_focus(cs3_t * s)
2650141cc406Sopenharmony_ci{
2651141cc406Sopenharmony_ci	DBG(6, "%s: setting focus to %d\n", __func__, s->focus);
2652141cc406Sopenharmony_ci
2653141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_READY);
2654141cc406Sopenharmony_ci	cs3_init_buffer(s);
2655141cc406Sopenharmony_ci	cs3_parse_cmd(s, "e0 00 c1 00 00 00 00 00 09 00 00");
2656141cc406Sopenharmony_ci	cs3_pack_long(s, s->focus);
2657141cc406Sopenharmony_ci	cs3_parse_cmd(s, "00 00 00 00");
2658141cc406Sopenharmony_ci
2659141cc406Sopenharmony_ci	return cs3_issue_and_execute(s);
2660141cc406Sopenharmony_ci}
2661141cc406Sopenharmony_ci
2662141cc406Sopenharmony_cistatic SANE_Status
2663141cc406Sopenharmony_cics3_read_focus(cs3_t * s)
2664141cc406Sopenharmony_ci{
2665141cc406Sopenharmony_ci	SANE_Status status;
2666141cc406Sopenharmony_ci
2667141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_READY);
2668141cc406Sopenharmony_ci	cs3_init_buffer(s);
2669141cc406Sopenharmony_ci	cs3_parse_cmd(s, "e1 00 c1 00 00 00 00 00 0d 00");
2670141cc406Sopenharmony_ci	s->n_recv = 13;
2671141cc406Sopenharmony_ci
2672141cc406Sopenharmony_ci	status = cs3_issue_cmd(s);
2673141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2674141cc406Sopenharmony_ci		return status;
2675141cc406Sopenharmony_ci
2676141cc406Sopenharmony_ci	s->focus =
2677141cc406Sopenharmony_ci		65536 * (256 * s->recv_buf[1] + s->recv_buf[2]) +
2678141cc406Sopenharmony_ci		256 * s->recv_buf[3] + s->recv_buf[4];
2679141cc406Sopenharmony_ci
2680141cc406Sopenharmony_ci	DBG(4, "%s: focus at %d\n", __func__, s->focus);
2681141cc406Sopenharmony_ci
2682141cc406Sopenharmony_ci	return status;
2683141cc406Sopenharmony_ci}
2684141cc406Sopenharmony_ci
2685141cc406Sopenharmony_cistatic SANE_Status
2686141cc406Sopenharmony_cics3_autofocus(cs3_t * s)
2687141cc406Sopenharmony_ci{
2688141cc406Sopenharmony_ci	SANE_Status status;
2689141cc406Sopenharmony_ci
2690141cc406Sopenharmony_ci	DBG(6, "%s: focusing at %ld,%ld\n", __func__,
2691141cc406Sopenharmony_ci	    s->real_focusx, s->real_focusy);
2692141cc406Sopenharmony_ci
2693141cc406Sopenharmony_ci	cs3_convert_options(s);
2694141cc406Sopenharmony_ci
2695141cc406Sopenharmony_ci	status = cs3_read_focus(s);
2696141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2697141cc406Sopenharmony_ci		return status;
2698141cc406Sopenharmony_ci
2699141cc406Sopenharmony_ci	/* set parameter, autofocus */
2700141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_READY);
2701141cc406Sopenharmony_ci	cs3_init_buffer(s);
2702141cc406Sopenharmony_ci	cs3_parse_cmd(s, "e0 00 a0 00 00 00 00 00 09 00 00");
2703141cc406Sopenharmony_ci	cs3_pack_long(s, s->real_focusx);
2704141cc406Sopenharmony_ci	cs3_pack_long(s, s->real_focusy);
2705141cc406Sopenharmony_ci	/*cs3_parse_cmd(s, "00 00 00 00"); */
2706141cc406Sopenharmony_ci
2707141cc406Sopenharmony_ci	status = cs3_issue_and_execute(s);
2708141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2709141cc406Sopenharmony_ci		return status;
2710141cc406Sopenharmony_ci
2711141cc406Sopenharmony_ci	return cs3_read_focus(s);
2712141cc406Sopenharmony_ci}
2713141cc406Sopenharmony_ci
2714141cc406Sopenharmony_cistatic SANE_Status
2715141cc406Sopenharmony_cics3_autoexposure(cs3_t * s, int wb)
2716141cc406Sopenharmony_ci{
2717141cc406Sopenharmony_ci	SANE_Status status;
2718141cc406Sopenharmony_ci
2719141cc406Sopenharmony_ci	DBG(6, "%s, wb = %d\n", __func__, wb);
2720141cc406Sopenharmony_ci
2721141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2722141cc406Sopenharmony_ci	status = cs3_scan(s, wb ? CS3_SCAN_AE_WB : CS3_SCAN_AE);
2723141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2724141cc406Sopenharmony_ci		return status;
2725141cc406Sopenharmony_ci
2726141cc406Sopenharmony_ci	status = cs3_get_exposure(s);
2727141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2728141cc406Sopenharmony_ci		return status;
2729141cc406Sopenharmony_ci
2730141cc406Sopenharmony_ci	s->exposure = 1.;
2731141cc406Sopenharmony_ci	s->exposure_r = s->real_exposure[1] / 100.;
2732141cc406Sopenharmony_ci	s->exposure_g = s->real_exposure[2] / 100.;
2733141cc406Sopenharmony_ci	s->exposure_b = s->real_exposure[3] / 100.;
2734141cc406Sopenharmony_ci
2735141cc406Sopenharmony_ci	return status;
2736141cc406Sopenharmony_ci}
2737141cc406Sopenharmony_ci
2738141cc406Sopenharmony_cistatic SANE_Status
2739141cc406Sopenharmony_cics3_get_exposure(cs3_t * s)
2740141cc406Sopenharmony_ci{
2741141cc406Sopenharmony_ci	SANE_Status status;
2742141cc406Sopenharmony_ci	int i_color, colors = s->n_colors;
2743141cc406Sopenharmony_ci
2744141cc406Sopenharmony_ci	DBG(6, "%s\n", __func__);
2745141cc406Sopenharmony_ci
2746141cc406Sopenharmony_ci	if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000))
2747141cc406Sopenharmony_ci		colors = 3;
2748141cc406Sopenharmony_ci
2749141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
2750141cc406Sopenharmony_ci
2751141cc406Sopenharmony_ci	/* GET WINDOW */
2752141cc406Sopenharmony_ci	for (i_color = 0; i_color < colors; i_color++) {	/* XXXXXXXXXXXXX CCCCCCCCCCCCC */
2753141cc406Sopenharmony_ci
2754141cc406Sopenharmony_ci		cs3_init_buffer(s);
2755141cc406Sopenharmony_ci		cs3_parse_cmd(s, "25 01 00 00 00");
2756141cc406Sopenharmony_ci		cs3_pack_byte(s, cs3_colors[i_color]);
2757141cc406Sopenharmony_ci		cs3_parse_cmd(s, "00 00 3a 00");
2758141cc406Sopenharmony_ci		s->n_recv = 58;
2759141cc406Sopenharmony_ci		status = cs3_issue_cmd(s);
2760141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
2761141cc406Sopenharmony_ci			return status;
2762141cc406Sopenharmony_ci
2763141cc406Sopenharmony_ci		s->real_exposure[cs3_colors[i_color]] =
2764141cc406Sopenharmony_ci			65536 * (256 * s->recv_buf[54] + s->recv_buf[55]) +
2765141cc406Sopenharmony_ci			256 * s->recv_buf[56] + s->recv_buf[57];
2766141cc406Sopenharmony_ci
2767141cc406Sopenharmony_ci		DBG(6,
2768141cc406Sopenharmony_ci		    "%s, exposure for color %i: %li * 10ns\n",
2769141cc406Sopenharmony_ci		    __func__,
2770141cc406Sopenharmony_ci		    cs3_colors[i_color],
2771141cc406Sopenharmony_ci		    s->real_exposure[cs3_colors[i_color]]);
2772141cc406Sopenharmony_ci
2773141cc406Sopenharmony_ci		DBG(6, "%02x %02x %02x %02x\n", s->recv_buf[48],
2774141cc406Sopenharmony_ci		    s->recv_buf[49], s->recv_buf[50], s->recv_buf[51]);
2775141cc406Sopenharmony_ci	}
2776141cc406Sopenharmony_ci
2777141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2778141cc406Sopenharmony_ci}
2779141cc406Sopenharmony_ci
2780141cc406Sopenharmony_cistatic SANE_Status
2781141cc406Sopenharmony_cics3_convert_options(cs3_t * s)
2782141cc406Sopenharmony_ci{
2783141cc406Sopenharmony_ci	int i_color;
2784141cc406Sopenharmony_ci	unsigned long xmin, xmax, ymin, ymax;
2785141cc406Sopenharmony_ci
2786141cc406Sopenharmony_ci	DBG(4, "%s\n", __func__);
2787141cc406Sopenharmony_ci
2788141cc406Sopenharmony_ci	s->real_depth = (s->preview ? 8 : s->depth);
2789141cc406Sopenharmony_ci	s->bytes_per_pixel = (s->real_depth > 8 ? 2 : 1);
2790141cc406Sopenharmony_ci	s->shift_bits = 8 * s->bytes_per_pixel - s->real_depth;
2791141cc406Sopenharmony_ci
2792141cc406Sopenharmony_ci	DBG(12, " depth = %d, bpp = %d, shift = %d\n",
2793141cc406Sopenharmony_ci	    s->real_depth, s->bytes_per_pixel, s->shift_bits);
2794141cc406Sopenharmony_ci
2795141cc406Sopenharmony_ci	if (s->preview) {
2796141cc406Sopenharmony_ci		s->real_resx = s->res_preview;
2797141cc406Sopenharmony_ci		s->real_resy = s->res_preview;
2798141cc406Sopenharmony_ci	} else if (s->res_independent) {
2799141cc406Sopenharmony_ci		s->real_resx = s->resx;
2800141cc406Sopenharmony_ci		s->real_resy = s->resy;
2801141cc406Sopenharmony_ci	} else {
2802141cc406Sopenharmony_ci		s->real_resx = s->res;
2803141cc406Sopenharmony_ci		s->real_resy = s->res;
2804141cc406Sopenharmony_ci	}
2805141cc406Sopenharmony_ci
2806141cc406Sopenharmony_ci	s->real_pitchx = s->resx_max / s->real_resx;
2807141cc406Sopenharmony_ci	s->real_pitchy = s->resy_max / s->real_resy;
2808141cc406Sopenharmony_ci
2809141cc406Sopenharmony_ci	s->real_resx = s->resx_max / s->real_pitchx;
2810141cc406Sopenharmony_ci	s->real_resy = s->resy_max / s->real_pitchy;
2811141cc406Sopenharmony_ci
2812141cc406Sopenharmony_ci	DBG(12, " resx = %d, resy = %d, pitchx = %d, pitchy = %d\n",
2813141cc406Sopenharmony_ci	    s->real_resx, s->real_resy, s->real_pitchx, s->real_pitchy);
2814141cc406Sopenharmony_ci
2815141cc406Sopenharmony_ci	/* The prefix "real_" refers to data in device units (1/maxdpi),
2816141cc406Sopenharmony_ci	 * "logical_" refers to resolution-dependent data.
2817141cc406Sopenharmony_ci	 */
2818141cc406Sopenharmony_ci
2819141cc406Sopenharmony_ci	if (s->xmin < s->xmax) {
2820141cc406Sopenharmony_ci		xmin = s->xmin;
2821141cc406Sopenharmony_ci		xmax = s->xmax;
2822141cc406Sopenharmony_ci	} else {
2823141cc406Sopenharmony_ci		xmin = s->xmax;
2824141cc406Sopenharmony_ci		xmax = s->xmin;
2825141cc406Sopenharmony_ci	}
2826141cc406Sopenharmony_ci
2827141cc406Sopenharmony_ci	if (s->ymin < s->ymax) {
2828141cc406Sopenharmony_ci		ymin = s->ymin;
2829141cc406Sopenharmony_ci		ymax = s->ymax;
2830141cc406Sopenharmony_ci	} else {
2831141cc406Sopenharmony_ci		ymin = s->ymax;
2832141cc406Sopenharmony_ci		ymax = s->ymin;
2833141cc406Sopenharmony_ci	}
2834141cc406Sopenharmony_ci
2835141cc406Sopenharmony_ci	DBG(12, " xmin = %ld, xmax = %ld\n", xmin, xmax);
2836141cc406Sopenharmony_ci	DBG(12, " ymin = %ld, ymax = %ld\n", ymin, ymax);
2837141cc406Sopenharmony_ci
2838141cc406Sopenharmony_ci	s->real_xoffset = xmin;
2839141cc406Sopenharmony_ci	s->real_yoffset =
2840141cc406Sopenharmony_ci		ymin + (s->i_frame - 1) * s->frame_offset +
2841141cc406Sopenharmony_ci		s->subframe / s->unit_mm;
2842141cc406Sopenharmony_ci
2843141cc406Sopenharmony_ci	DBG(12, " xoffset = %ld, yoffset = %ld\n",
2844141cc406Sopenharmony_ci	    s->real_xoffset, s->real_yoffset);
2845141cc406Sopenharmony_ci
2846141cc406Sopenharmony_ci
2847141cc406Sopenharmony_ci	s->logical_width = (xmax - xmin + 1) / s->real_pitchx;	/* XXX use mm units */
2848141cc406Sopenharmony_ci	s->logical_height = (ymax - ymin + 1) / s->real_pitchy;
2849141cc406Sopenharmony_ci	s->real_width = s->logical_width * s->real_pitchx;
2850141cc406Sopenharmony_ci	s->real_height = s->logical_height * s->real_pitchy;
2851141cc406Sopenharmony_ci
2852141cc406Sopenharmony_ci	DBG(12, " lw = %ld, lh = %ld, rw = %ld, rh = %ld\n",
2853141cc406Sopenharmony_ci	    s->logical_width, s->logical_height,
2854141cc406Sopenharmony_ci	    s->real_width, s->real_height);
2855141cc406Sopenharmony_ci
2856141cc406Sopenharmony_ci	s->odd_padding = 0;
2857141cc406Sopenharmony_ci	if ((s->bytes_per_pixel == 1) && (s->logical_width & 0x01)
2858141cc406Sopenharmony_ci	    && (s->type != CS3_TYPE_LS30) && (s->type != CS3_TYPE_LS2000))
2859141cc406Sopenharmony_ci		s->odd_padding = 1;
2860141cc406Sopenharmony_ci
2861141cc406Sopenharmony_ci	if (s->focus_on_centre) {
2862141cc406Sopenharmony_ci		s->real_focusx = s->real_xoffset + s->real_width / 2;
2863141cc406Sopenharmony_ci		s->real_focusy = s->real_yoffset + s->real_height / 2;
2864141cc406Sopenharmony_ci	} else {
2865141cc406Sopenharmony_ci		s->real_focusx = s->focusx;
2866141cc406Sopenharmony_ci		s->real_focusy =
2867141cc406Sopenharmony_ci			s->focusy + (s->i_frame - 1) * s->frame_offset +
2868141cc406Sopenharmony_ci			s->subframe / s->unit_mm;
2869141cc406Sopenharmony_ci	}
2870141cc406Sopenharmony_ci
2871141cc406Sopenharmony_ci	DBG(12, " focusx = %ld, focusy = %ld\n",
2872141cc406Sopenharmony_ci	    s->real_focusx, s->real_focusy);
2873141cc406Sopenharmony_ci
2874141cc406Sopenharmony_ci	s->real_exposure[1] = s->exposure * s->exposure_r * 100.;
2875141cc406Sopenharmony_ci	s->real_exposure[2] = s->exposure * s->exposure_g * 100.;
2876141cc406Sopenharmony_ci	s->real_exposure[3] = s->exposure * s->exposure_b * 100.;
2877141cc406Sopenharmony_ci
2878141cc406Sopenharmony_ci	/* XXX IR? */
2879141cc406Sopenharmony_ci	for (i_color = 0; i_color < 3; i_color++)
2880141cc406Sopenharmony_ci		if (s->real_exposure[cs3_colors[i_color]] < 1)
2881141cc406Sopenharmony_ci			s->real_exposure[cs3_colors[i_color]] = 1;
2882141cc406Sopenharmony_ci
2883141cc406Sopenharmony_ci	s->n_colors = 3;	/* XXXXXXXXXXXXXX CCCCCCCCCCCCCC */
2884141cc406Sopenharmony_ci	if (s->infrared)
2885141cc406Sopenharmony_ci		s->n_colors = 4;
2886141cc406Sopenharmony_ci
2887141cc406Sopenharmony_ci	s->xfer_bytes_total =
2888141cc406Sopenharmony_ci		s->bytes_per_pixel * s->n_colors * s->logical_width *
2889141cc406Sopenharmony_ci		s->logical_height;
2890141cc406Sopenharmony_ci
2891141cc406Sopenharmony_ci	if (s->preview)
2892141cc406Sopenharmony_ci		s->infrared = SANE_FALSE;
2893141cc406Sopenharmony_ci
2894141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2895141cc406Sopenharmony_ci}
2896141cc406Sopenharmony_ci
2897141cc406Sopenharmony_cistatic SANE_Status
2898141cc406Sopenharmony_cics3_set_boundary(cs3_t * s)
2899141cc406Sopenharmony_ci{
2900141cc406Sopenharmony_ci	SANE_Status status;
2901141cc406Sopenharmony_ci	int i_boundary;
2902141cc406Sopenharmony_ci
2903141cc406Sopenharmony_ci	/* Ariel - Check this function */
2904141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_READY);
2905141cc406Sopenharmony_ci	cs3_init_buffer(s);
2906141cc406Sopenharmony_ci	cs3_parse_cmd(s, "2a 00 88 00 00 03");
2907141cc406Sopenharmony_ci	cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 16) & 0xff);
2908141cc406Sopenharmony_ci	cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff);
2909141cc406Sopenharmony_ci	cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff);
2910141cc406Sopenharmony_ci	cs3_parse_cmd(s, "00");
2911141cc406Sopenharmony_ci
2912141cc406Sopenharmony_ci	cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff);
2913141cc406Sopenharmony_ci	cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff);
2914141cc406Sopenharmony_ci	cs3_pack_byte(s, s->n_frames);
2915141cc406Sopenharmony_ci	cs3_pack_byte(s, s->n_frames);
2916141cc406Sopenharmony_ci	for (i_boundary = 0; i_boundary < s->n_frames; i_boundary++) {
2917141cc406Sopenharmony_ci		unsigned long lvalue = s->frame_offset * i_boundary +
2918141cc406Sopenharmony_ci			s->subframe / s->unit_mm;
2919141cc406Sopenharmony_ci
2920141cc406Sopenharmony_ci		cs3_pack_long(s, lvalue);
2921141cc406Sopenharmony_ci
2922141cc406Sopenharmony_ci		cs3_pack_long(s, 0);
2923141cc406Sopenharmony_ci
2924141cc406Sopenharmony_ci		lvalue = s->frame_offset * i_boundary +
2925141cc406Sopenharmony_ci			s->subframe / s->unit_mm + s->frame_offset - 1;
2926141cc406Sopenharmony_ci		cs3_pack_long(s, lvalue);
2927141cc406Sopenharmony_ci
2928141cc406Sopenharmony_ci		cs3_pack_long(s, s->boundaryx - 1);
2929141cc406Sopenharmony_ci
2930141cc406Sopenharmony_ci	}
2931141cc406Sopenharmony_ci	status = cs3_issue_cmd(s);
2932141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2933141cc406Sopenharmony_ci		return status;
2934141cc406Sopenharmony_ci
2935141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2936141cc406Sopenharmony_ci}
2937141cc406Sopenharmony_ci
2938141cc406Sopenharmony_cistatic SANE_Status
2939141cc406Sopenharmony_cics3_send_lut(cs3_t * s)
2940141cc406Sopenharmony_ci{
2941141cc406Sopenharmony_ci	int color;
2942141cc406Sopenharmony_ci	SANE_Status status;
2943141cc406Sopenharmony_ci	cs3_pixel_t *lut, pixel;
2944141cc406Sopenharmony_ci
2945141cc406Sopenharmony_ci	DBG(6, "%s\n", __func__);
2946141cc406Sopenharmony_ci
2947141cc406Sopenharmony_ci	for (color = 0; color < s->n_colors; color++) {
2948141cc406Sopenharmony_ci		/*cs3_scanner_ready(s, CS3_STATUS_READY); */
2949141cc406Sopenharmony_ci
2950141cc406Sopenharmony_ci		switch (color) {
2951141cc406Sopenharmony_ci		case 0:
2952141cc406Sopenharmony_ci			lut = s->lut_r;
2953141cc406Sopenharmony_ci			break;
2954141cc406Sopenharmony_ci		case 1:
2955141cc406Sopenharmony_ci			lut = s->lut_g;
2956141cc406Sopenharmony_ci			break;
2957141cc406Sopenharmony_ci		case 2:
2958141cc406Sopenharmony_ci			lut = s->lut_b;
2959141cc406Sopenharmony_ci			break;
2960141cc406Sopenharmony_ci		case 3:
2961141cc406Sopenharmony_ci			lut = s->lut_neutral;
2962141cc406Sopenharmony_ci			break;
2963141cc406Sopenharmony_ci		default:
2964141cc406Sopenharmony_ci			DBG(1,
2965141cc406Sopenharmony_ci			    "BUG: %s: Unknown color number for LUT download.\n",
2966141cc406Sopenharmony_ci			    __func__);
2967141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
2968141cc406Sopenharmony_ci			break;
2969141cc406Sopenharmony_ci		}
2970141cc406Sopenharmony_ci
2971141cc406Sopenharmony_ci		cs3_init_buffer(s);
2972141cc406Sopenharmony_ci		cs3_parse_cmd(s, "2a 00 03 00");
2973141cc406Sopenharmony_ci		cs3_pack_byte(s, cs3_colors[color]);
2974141cc406Sopenharmony_ci		cs3_pack_byte(s, 2 - 1);	/* XXX number of bytes per data point - 1 */
2975141cc406Sopenharmony_ci		cs3_pack_byte(s, ((2 * s->n_lut) >> 16) & 0xff);	/* XXX 2 bytes per point */
2976141cc406Sopenharmony_ci		cs3_pack_byte(s, ((2 * s->n_lut) >> 8) & 0xff);	/* XXX 2 bytes per point */
2977141cc406Sopenharmony_ci		cs3_pack_byte(s, (2 * s->n_lut) & 0xff);	/* XXX 2 bytes per point */
2978141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x00);
2979141cc406Sopenharmony_ci
2980141cc406Sopenharmony_ci		for (pixel = 0; pixel < s->n_lut; pixel++) {	/* XXX 2 bytes per point */
2981141cc406Sopenharmony_ci			cs3_pack_word(s, lut[pixel]);
2982141cc406Sopenharmony_ci		}
2983141cc406Sopenharmony_ci
2984141cc406Sopenharmony_ci		status = cs3_issue_cmd(s);
2985141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
2986141cc406Sopenharmony_ci			return status;
2987141cc406Sopenharmony_ci	}
2988141cc406Sopenharmony_ci
2989141cc406Sopenharmony_ci	return status;
2990141cc406Sopenharmony_ci}
2991141cc406Sopenharmony_ci
2992141cc406Sopenharmony_cistatic SANE_Status
2993141cc406Sopenharmony_cics3_set_window(cs3_t * s, cs3_scan_t type)
2994141cc406Sopenharmony_ci{
2995141cc406Sopenharmony_ci	int color;
2996141cc406Sopenharmony_ci	SANE_Status status = SANE_STATUS_INVAL;
2997141cc406Sopenharmony_ci
2998141cc406Sopenharmony_ci	/* SET WINDOW */
2999141cc406Sopenharmony_ci	for (color = 0; color < s->n_colors; color++) {
3000141cc406Sopenharmony_ci
3001141cc406Sopenharmony_ci		DBG(8, "%s: color %d\n", __func__, cs3_colors[color]);
3002141cc406Sopenharmony_ci
3003141cc406Sopenharmony_ci		cs3_scanner_ready(s, CS3_STATUS_READY);
3004141cc406Sopenharmony_ci
3005141cc406Sopenharmony_ci		cs3_init_buffer(s);
3006141cc406Sopenharmony_ci		if ((s->type == CS3_TYPE_LS40)
3007141cc406Sopenharmony_ci		    || (s->type == CS3_TYPE_LS4000)
3008141cc406Sopenharmony_ci		    || (s->type == CS3_TYPE_LS50)
3009141cc406Sopenharmony_ci		    || (s->type == CS3_TYPE_LS5000))
3010141cc406Sopenharmony_ci			cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 80");
3011141cc406Sopenharmony_ci		else
3012141cc406Sopenharmony_ci			cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 00");
3013141cc406Sopenharmony_ci
3014141cc406Sopenharmony_ci		cs3_parse_cmd(s, "00 00 00 00 00 00 00 32");
3015141cc406Sopenharmony_ci
3016141cc406Sopenharmony_ci		cs3_pack_byte(s, cs3_colors[color]);
3017141cc406Sopenharmony_ci
3018141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x00);
3019141cc406Sopenharmony_ci
3020141cc406Sopenharmony_ci		cs3_pack_word(s, s->real_resx);
3021141cc406Sopenharmony_ci		cs3_pack_word(s, s->real_resy);
3022141cc406Sopenharmony_ci		cs3_pack_long(s, s->real_xoffset);
3023141cc406Sopenharmony_ci		cs3_pack_long(s, s->real_yoffset);
3024141cc406Sopenharmony_ci		cs3_pack_long(s, s->real_width);
3025141cc406Sopenharmony_ci		cs3_pack_long(s, s->real_height);
3026141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x00);	/* brightness, etc. */
3027141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x00);
3028141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x00);
3029141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x05);	/* image composition CCCCCCC */
3030141cc406Sopenharmony_ci		cs3_pack_byte(s, s->real_depth);	/* pixel composition */
3031141cc406Sopenharmony_ci		cs3_parse_cmd(s, "00 00 00 00 00 00 00 00 00 00 00 00 00");
3032141cc406Sopenharmony_ci		cs3_pack_byte(s, ((s->samples_per_scan - 1) << 4) | 0x00);	/* multiread, ordering */
3033141cc406Sopenharmony_ci
3034141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x80 | (s->negative ? 0 : 1));	/* averaging, pos/neg */
3035141cc406Sopenharmony_ci
3036141cc406Sopenharmony_ci		switch (type) {	/* scanning kind */
3037141cc406Sopenharmony_ci		case CS3_SCAN_NORMAL:
3038141cc406Sopenharmony_ci			cs3_pack_byte(s, 0x01);
3039141cc406Sopenharmony_ci			break;
3040141cc406Sopenharmony_ci		case CS3_SCAN_AE:
3041141cc406Sopenharmony_ci			cs3_pack_byte(s, 0x20);
3042141cc406Sopenharmony_ci			break;
3043141cc406Sopenharmony_ci		case CS3_SCAN_AE_WB:
3044141cc406Sopenharmony_ci			cs3_pack_byte(s, 0x40);
3045141cc406Sopenharmony_ci			break;
3046141cc406Sopenharmony_ci		default:
3047141cc406Sopenharmony_ci			DBG(1, "BUG: cs3_scan(): Unknown scanning type.\n");
3048141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
3049141cc406Sopenharmony_ci		}
3050141cc406Sopenharmony_ci		if (s->samples_per_scan == 1)
3051141cc406Sopenharmony_ci			cs3_pack_byte(s, 0x02);	/* scanning mode single */
3052141cc406Sopenharmony_ci		else
3053141cc406Sopenharmony_ci			cs3_pack_byte(s, 0x10);	/* scanning mode multi */
3054141cc406Sopenharmony_ci		cs3_pack_byte(s, 0x02);	/* color interleaving */
3055141cc406Sopenharmony_ci		cs3_pack_byte(s, 0xff);	/* (ae) */
3056141cc406Sopenharmony_ci		if (color == 3)	/* infrared */
3057141cc406Sopenharmony_ci			cs3_parse_cmd(s, "00 00 00 00");	/* automatic */
3058141cc406Sopenharmony_ci		else {
3059141cc406Sopenharmony_ci			DBG(4, "%s: exposure = %ld * 10ns\n", __func__,
3060141cc406Sopenharmony_ci			    s->real_exposure[cs3_colors[color]]);
3061141cc406Sopenharmony_ci			cs3_pack_long(s, s->real_exposure[cs3_colors[color]]);
3062141cc406Sopenharmony_ci		}
3063141cc406Sopenharmony_ci
3064141cc406Sopenharmony_ci		status = cs3_issue_cmd(s);
3065141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
3066141cc406Sopenharmony_ci			return status;
3067141cc406Sopenharmony_ci	}
3068141cc406Sopenharmony_ci
3069141cc406Sopenharmony_ci	return status;
3070141cc406Sopenharmony_ci}
3071141cc406Sopenharmony_ci
3072141cc406Sopenharmony_ci
3073141cc406Sopenharmony_cistatic SANE_Status
3074141cc406Sopenharmony_cics3_scan(cs3_t * s, cs3_scan_t type)
3075141cc406Sopenharmony_ci{
3076141cc406Sopenharmony_ci	SANE_Status status;
3077141cc406Sopenharmony_ci
3078141cc406Sopenharmony_ci	s->block_padding = 0;
3079141cc406Sopenharmony_ci
3080141cc406Sopenharmony_ci	DBG(6, "%s, type = %d, colors = %d\n", __func__, type, s->n_colors);
3081141cc406Sopenharmony_ci
3082141cc406Sopenharmony_ci	switch (type) {
3083141cc406Sopenharmony_ci	case CS3_SCAN_NORMAL:
3084141cc406Sopenharmony_ci		DBG(16, "%s: normal scan\n", __func__);
3085141cc406Sopenharmony_ci		break;
3086141cc406Sopenharmony_ci	case CS3_SCAN_AE:
3087141cc406Sopenharmony_ci		DBG(16, "%s: ae scan\n", __func__);
3088141cc406Sopenharmony_ci		break;
3089141cc406Sopenharmony_ci	case CS3_SCAN_AE_WB:
3090141cc406Sopenharmony_ci		DBG(16, "%s: ae wb scan\n", __func__);
3091141cc406Sopenharmony_ci		break;
3092141cc406Sopenharmony_ci	}
3093141cc406Sopenharmony_ci
3094141cc406Sopenharmony_ci	/* wait for device to be ready with document, and set device unit */
3095141cc406Sopenharmony_ci	status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS);
3096141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
3097141cc406Sopenharmony_ci		return status;
3098141cc406Sopenharmony_ci
3099141cc406Sopenharmony_ci	if (s->status & CS3_STATUS_NO_DOCS)
3100141cc406Sopenharmony_ci		return SANE_STATUS_NO_DOCS;
3101141cc406Sopenharmony_ci
3102141cc406Sopenharmony_ci	status = cs3_convert_options(s);
3103141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
3104141cc406Sopenharmony_ci		return status;
3105141cc406Sopenharmony_ci
3106141cc406Sopenharmony_ci	status = cs3_set_boundary(s);
3107141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
3108141cc406Sopenharmony_ci		return status;
3109141cc406Sopenharmony_ci
3110141cc406Sopenharmony_ci	cs3_set_focus(s);
3111141cc406Sopenharmony_ci
3112141cc406Sopenharmony_ci	cs3_scanner_ready(s, CS3_STATUS_READY);
3113141cc406Sopenharmony_ci
3114141cc406Sopenharmony_ci	if (type == CS3_SCAN_NORMAL)
3115141cc406Sopenharmony_ci		cs3_send_lut(s);
3116141cc406Sopenharmony_ci
3117141cc406Sopenharmony_ci	status = cs3_set_window(s, type);
3118141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
3119141cc406Sopenharmony_ci		return status;
3120141cc406Sopenharmony_ci
3121141cc406Sopenharmony_ci	status = cs3_get_exposure(s);
3122141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
3123141cc406Sopenharmony_ci		return status;
3124141cc406Sopenharmony_ci
3125141cc406Sopenharmony_ci/*	cs3_scanner_ready(s, CS3_STATUS_READY); */
3126141cc406Sopenharmony_ci
3127141cc406Sopenharmony_ci	cs3_init_buffer(s);
3128141cc406Sopenharmony_ci	switch (s->n_colors) {
3129141cc406Sopenharmony_ci	case 3:
3130141cc406Sopenharmony_ci		cs3_parse_cmd(s, "1b 00 00 00 03 00 01 02 03");
3131141cc406Sopenharmony_ci		break;
3132141cc406Sopenharmony_ci	case 4:
3133141cc406Sopenharmony_ci		cs3_parse_cmd(s, "1b 00 00 00 04 00 01 02 03 09");
3134141cc406Sopenharmony_ci		break;
3135141cc406Sopenharmony_ci	default:
3136141cc406Sopenharmony_ci		DBG(0, "BUG: %s: Unknown number of input colors.\n",
3137141cc406Sopenharmony_ci		    __func__);
3138141cc406Sopenharmony_ci		break;
3139141cc406Sopenharmony_ci	}
3140141cc406Sopenharmony_ci
3141141cc406Sopenharmony_ci	status = cs3_issue_cmd(s);
3142141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
3143141cc406Sopenharmony_ci		DBG(6, "scan setup failed\n");
3144141cc406Sopenharmony_ci		return status;
3145141cc406Sopenharmony_ci	}
3146141cc406Sopenharmony_ci
3147141cc406Sopenharmony_ci	if (s->status == CS3_STATUS_REISSUE) {
3148141cc406Sopenharmony_ci		status = cs3_issue_cmd(s);
3149141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
3150141cc406Sopenharmony_ci			return status;
3151141cc406Sopenharmony_ci	}
3152141cc406Sopenharmony_ci
3153141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
3154141cc406Sopenharmony_ci}
3155141cc406Sopenharmony_ci
3156141cc406Sopenharmony_cistatic void *
3157141cc406Sopenharmony_cics3_xmalloc(size_t size)
3158141cc406Sopenharmony_ci{
3159141cc406Sopenharmony_ci	register void *value = malloc(size);
3160141cc406Sopenharmony_ci
3161141cc406Sopenharmony_ci	if (value == NULL) {
3162141cc406Sopenharmony_ci		DBG(0, "error: %s: failed to malloc() %lu bytes.\n",
3163141cc406Sopenharmony_ci		    __func__, (unsigned long) size);
3164141cc406Sopenharmony_ci	}
3165141cc406Sopenharmony_ci	return value;
3166141cc406Sopenharmony_ci}
3167141cc406Sopenharmony_ci
3168141cc406Sopenharmony_cistatic void *
3169141cc406Sopenharmony_cics3_xrealloc(void *p, size_t size)
3170141cc406Sopenharmony_ci{
3171141cc406Sopenharmony_ci	register void *value;
3172141cc406Sopenharmony_ci
3173141cc406Sopenharmony_ci	if (!size)
3174141cc406Sopenharmony_ci		return p;
3175141cc406Sopenharmony_ci
3176141cc406Sopenharmony_ci	value = realloc(p, size);
3177141cc406Sopenharmony_ci
3178141cc406Sopenharmony_ci	if (value == NULL) {
3179141cc406Sopenharmony_ci		DBG(0, "error: %s: failed to realloc() %lu bytes.\n",
3180141cc406Sopenharmony_ci		    __func__, (unsigned long) size);
3181141cc406Sopenharmony_ci	}
3182141cc406Sopenharmony_ci
3183141cc406Sopenharmony_ci	return value;
3184141cc406Sopenharmony_ci}
3185141cc406Sopenharmony_ci
3186141cc406Sopenharmony_cistatic void
3187141cc406Sopenharmony_cics3_xfree(void *p)
3188141cc406Sopenharmony_ci{
3189141cc406Sopenharmony_ci	if (p)
3190141cc406Sopenharmony_ci          free(p);
3191141cc406Sopenharmony_ci}
3192