1141cc406Sopenharmony_ci/*
2141cc406Sopenharmony_ci * magicolor.c - SANE library for Magicolor scanners.
3141cc406Sopenharmony_ci *
4141cc406Sopenharmony_ci * (C) 2010 Reinhold Kainhofer <reinhold@kainhofer.com>
5141cc406Sopenharmony_ci *
6141cc406Sopenharmony_ci * Based on the epson2 sane backend:
7141cc406Sopenharmony_ci * Based on Kazuhiro Sasayama previous
8141cc406Sopenharmony_ci * Work on epson.[ch] file from the SANE package.
9141cc406Sopenharmony_ci * Please see those files for additional copyrights.
10141cc406Sopenharmony_ci * Copyright (C) 2006-10 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#define MAGICOLOR_VERSION	0
21141cc406Sopenharmony_ci#define MAGICOLOR_REVISION	0
22141cc406Sopenharmony_ci#define MAGICOLOR_BUILD	1
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci/* debugging levels:
25141cc406Sopenharmony_ci *
26141cc406Sopenharmony_ci *     127	mc_recv buffer
27141cc406Sopenharmony_ci *     125	mc_send buffer
28141cc406Sopenharmony_ci *	35	fine-grained status and progress
29141cc406Sopenharmony_ci *	30	sane_read
30141cc406Sopenharmony_ci *	25	setvalue, getvalue, control_option
31141cc406Sopenharmony_ci *	20	low-level (I/O) mc_* functions
32141cc406Sopenharmony_ci *	15	mid-level mc_* functions
33141cc406Sopenharmony_ci *	10	high-level cmd_* functions
34141cc406Sopenharmony_ci *	 7	open/close/attach
35141cc406Sopenharmony_ci *	 6	print_params
36141cc406Sopenharmony_ci *	 5	basic functions
37141cc406Sopenharmony_ci *	 3	status info and progress
38141cc406Sopenharmony_ci *	 2	scanner info and capabilities
39141cc406Sopenharmony_ci *	 1	errors & warnings
40141cc406Sopenharmony_ci */
41141cc406Sopenharmony_ci
42141cc406Sopenharmony_ci#include "sane/config.h"
43141cc406Sopenharmony_ci
44141cc406Sopenharmony_ci#include <limits.h>
45141cc406Sopenharmony_ci#include <stdio.h>
46141cc406Sopenharmony_ci#include <string.h>
47141cc406Sopenharmony_ci#include <stdlib.h>
48141cc406Sopenharmony_ci#include <ctype.h>
49141cc406Sopenharmony_ci#include <fcntl.h>
50141cc406Sopenharmony_ci#include <unistd.h>
51141cc406Sopenharmony_ci#include <errno.h>
52141cc406Sopenharmony_ci#include <sys/time.h>
53141cc406Sopenharmony_ci#include <math.h>
54141cc406Sopenharmony_ci#include <poll.h>
55141cc406Sopenharmony_ci#include <sys/types.h>
56141cc406Sopenharmony_ci#ifdef HAVE_SYS_SOCKET_H
57141cc406Sopenharmony_ci#include <sys/socket.h>
58141cc406Sopenharmony_ci#endif
59141cc406Sopenharmony_ci
60141cc406Sopenharmony_ci
61141cc406Sopenharmony_ci#if HAVE_LIBSNMP
62141cc406Sopenharmony_ci#include <net-snmp/net-snmp-config.h>
63141cc406Sopenharmony_ci#include <net-snmp/net-snmp-includes.h>
64141cc406Sopenharmony_ci#include <net-snmp/library/snmp_transport.h>
65141cc406Sopenharmony_ci#include <arpa/inet.h>
66141cc406Sopenharmony_ci#endif
67141cc406Sopenharmony_ci
68141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
69141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
70141cc406Sopenharmony_ci#include "../include/sane/sanei_tcp.h"
71141cc406Sopenharmony_ci#include "../include/sane/sanei_udp.h"
72141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
73141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
74141cc406Sopenharmony_ci
75141cc406Sopenharmony_ci#include "magicolor.h"
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_ci
78141cc406Sopenharmony_ci#define min(x,y) (((x)<(y))?(x):(y))
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_ci
81141cc406Sopenharmony_ci
82141cc406Sopenharmony_ci/****************************************************************************
83141cc406Sopenharmony_ci *   Devices supported by this backend
84141cc406Sopenharmony_ci ****************************************************************************/
85141cc406Sopenharmony_ci
86141cc406Sopenharmony_ci
87141cc406Sopenharmony_ci
88141cc406Sopenharmony_ci/*          Scanner command type
89141cc406Sopenharmony_ci *          |     Start scan
90141cc406Sopenharmony_ci *          |     |     Poll for error
91141cc406Sopenharmony_ci *          |     |     |     Stop scan?
92141cc406Sopenharmony_ci *          |     |     |     |     Query image parameters
93141cc406Sopenharmony_ci *          |     |     |     |     |     set scan parameters
94141cc406Sopenharmony_ci *          |     |     |     |     |     |     Get status?
95141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     Read scanned data
96141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     Unknown
97141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     Unknown
98141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     |     Net wrapper command type
99141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     |     |     Net Welcome
100141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     |     |     |     Net Lock
101141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     |     |     |     |     Net Lock ACK
102141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     |     |     |     |     |     Net Unlock
103141cc406Sopenharmony_ci *          |     |     |     |     |     |     |     |     |     |     |     |     |     |     |
104141cc406Sopenharmony_ci*/
105141cc406Sopenharmony_cistatic struct MagicolorCmd magicolor_cmd[] = {
106141cc406Sopenharmony_ci  {"mc1690mf", CMD, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, NET, 0x00, 0x01, 0x02, 0x03},
107141cc406Sopenharmony_ci  {"mc4690mf", CMD, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, NET, 0x00, 0x01, 0x02, 0x03},
108141cc406Sopenharmony_ci};
109141cc406Sopenharmony_ci
110141cc406Sopenharmony_cistatic SANE_Int magicolor_default_resolutions[] = {150, 300, 600};
111141cc406Sopenharmony_cistatic SANE_Int magicolor_default_depths[] = {1,8};
112141cc406Sopenharmony_ci
113141cc406Sopenharmony_cistatic struct MagicolorCap magicolor_cap[] = {
114141cc406Sopenharmony_ci
115141cc406Sopenharmony_ci  /* KONICA MINOLTA magicolor 1690MF, USB ID 0x123b:2089 */
116141cc406Sopenharmony_ci  {
117141cc406Sopenharmony_ci      0x2089, "mc1690mf", "KONICA MINOLTA magicolor 1690MF", ".1.3.6.1.4.1.18334.1.1.1.1.1.23.1.1",
118141cc406Sopenharmony_ci      -1, 0x85,
119141cc406Sopenharmony_ci      600, {150, 600, 0}, magicolor_default_resolutions, 3,  /* 600 dpi max, 3 resolutions */
120141cc406Sopenharmony_ci      8, magicolor_default_depths,                          /* color depth 8 default, 1 and 8 possible */
121141cc406Sopenharmony_ci      {1, 9, 0},                                             /* brightness ranges (TODO!) */
122141cc406Sopenharmony_ci      {0, SANE_FIX(0x13f8 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x1b9c * MM_PER_INCH / 600), 0}, /* FBF x/y ranges (TODO!) */
123141cc406Sopenharmony_ci      SANE_TRUE, SANE_FALSE, /* non-duplex ADF, x/y ranges (TODO!) */
124141cc406Sopenharmony_ci      {0, SANE_FIX(0x1390 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x20dc * MM_PER_INCH / 600), 0},
125141cc406Sopenharmony_ci  },
126141cc406Sopenharmony_ci
127141cc406Sopenharmony_ci  /* KONICA MINOLTA magicolor 4690MF, USB ID 0x132b:2079 */
128141cc406Sopenharmony_ci  {
129141cc406Sopenharmony_ci      0x2079, "mc4690mf", "KONICA MINOLTA magicolor 4690MF",
130141cc406Sopenharmony_ci      "FIXME",                                              /* FIXME: fill in the correct OID! */
131141cc406Sopenharmony_ci      0x03, 0x85,
132141cc406Sopenharmony_ci      600, {150, 600, 0}, magicolor_default_resolutions, 3,  /* 600 dpi max, 3 resolutions */
133141cc406Sopenharmony_ci      8, magicolor_default_depths,                          /* color depth 8 default, 1 and 8 possible */
134141cc406Sopenharmony_ci      {1, 9, 0},                                             /* brightness ranges (TODO!) */
135141cc406Sopenharmony_ci      {0, SANE_FIX(0x13f8 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x1b9c * MM_PER_INCH / 600), 0}, /* FBF x/y ranges (TODO!) */
136141cc406Sopenharmony_ci      SANE_TRUE, SANE_TRUE, /* duplex ADF, x/y ranges (TODO!) */
137141cc406Sopenharmony_ci      {0, SANE_FIX(0x1390 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x20dc * MM_PER_INCH / 600), 0},
138141cc406Sopenharmony_ci  },
139141cc406Sopenharmony_ci
140141cc406Sopenharmony_ci};
141141cc406Sopenharmony_ci
142141cc406Sopenharmony_cistatic int MC_SNMP_Timeout = 2500;
143141cc406Sopenharmony_cistatic int MC_Scan_Data_Timeout = 15000;
144141cc406Sopenharmony_cistatic int MC_Request_Timeout = 5000;
145141cc406Sopenharmony_ci
146141cc406Sopenharmony_ci
147141cc406Sopenharmony_ci
148141cc406Sopenharmony_ci/****************************************************************************
149141cc406Sopenharmony_ci *   General configuration parameter definitions
150141cc406Sopenharmony_ci ****************************************************************************/
151141cc406Sopenharmony_ci
152141cc406Sopenharmony_ci
153141cc406Sopenharmony_ci/*
154141cc406Sopenharmony_ci * Definition of the mode_param struct, that is used to
155141cc406Sopenharmony_ci * specify the valid parameters for the different scan modes.
156141cc406Sopenharmony_ci *
157141cc406Sopenharmony_ci * The depth variable gets updated when the bit depth is modified.
158141cc406Sopenharmony_ci */
159141cc406Sopenharmony_ci
160141cc406Sopenharmony_cistatic struct mode_param mode_params[] = {
161141cc406Sopenharmony_ci	{0x00, 1, 1},  /* Lineart, 1 color, 1 bit */
162141cc406Sopenharmony_ci	{0x02, 1, 24}, /* Grayscale, 1 color, 24 bit */
163141cc406Sopenharmony_ci	{0x03, 3, 24}  /* Color, 3 colors, 24 bit */
164141cc406Sopenharmony_ci};
165141cc406Sopenharmony_ci
166141cc406Sopenharmony_cistatic SANE_String_Const mode_list[] = {
167141cc406Sopenharmony_ci	SANE_VALUE_SCAN_MODE_LINEART,
168141cc406Sopenharmony_ci	SANE_VALUE_SCAN_MODE_GRAY,
169141cc406Sopenharmony_ci	SANE_VALUE_SCAN_MODE_COLOR,
170141cc406Sopenharmony_ci	NULL
171141cc406Sopenharmony_ci};
172141cc406Sopenharmony_ci
173141cc406Sopenharmony_cistatic const SANE_String_Const adf_mode_list[] = {
174141cc406Sopenharmony_ci	SANE_I18N("Simplex"),
175141cc406Sopenharmony_ci	SANE_I18N("Duplex"),
176141cc406Sopenharmony_ci	NULL
177141cc406Sopenharmony_ci};
178141cc406Sopenharmony_ci
179141cc406Sopenharmony_ci/* Define the different scan sources */
180141cc406Sopenharmony_ci
181141cc406Sopenharmony_ci#define FBF_STR	SANE_I18N("Flatbed")
182141cc406Sopenharmony_ci#define ADF_STR	SANE_I18N("Automatic Document Feeder")
183141cc406Sopenharmony_ci
184141cc406Sopenharmony_ci/*
185141cc406Sopenharmony_ci * source list need one dummy entry (save device settings is crashing).
186141cc406Sopenharmony_ci * NOTE: no const - this list gets created while exploring the capabilities
187141cc406Sopenharmony_ci * of the scanner.
188141cc406Sopenharmony_ci */
189141cc406Sopenharmony_ci
190141cc406Sopenharmony_cistatic SANE_String_Const source_list[] = {
191141cc406Sopenharmony_ci	FBF_STR,
192141cc406Sopenharmony_ci	NULL,
193141cc406Sopenharmony_ci	NULL,
194141cc406Sopenharmony_ci	NULL
195141cc406Sopenharmony_ci};
196141cc406Sopenharmony_ci
197141cc406Sopenharmony_ci/* Some utility functions */
198141cc406Sopenharmony_ci
199141cc406Sopenharmony_cistatic size_t
200141cc406Sopenharmony_cimax_string_size(const SANE_String_Const strings[])
201141cc406Sopenharmony_ci{
202141cc406Sopenharmony_ci	size_t size, max_size = 0;
203141cc406Sopenharmony_ci	int i;
204141cc406Sopenharmony_ci
205141cc406Sopenharmony_ci	for (i = 0; strings[i]; i++) {
206141cc406Sopenharmony_ci		size = strlen(strings[i]) + 1;
207141cc406Sopenharmony_ci		if (size > max_size)
208141cc406Sopenharmony_ci			max_size = size;
209141cc406Sopenharmony_ci	}
210141cc406Sopenharmony_ci	return max_size;
211141cc406Sopenharmony_ci}
212141cc406Sopenharmony_ci
213141cc406Sopenharmony_cistatic SANE_Status attach_one_usb(SANE_String_Const devname);
214141cc406Sopenharmony_cistatic SANE_Status attach_one_net(SANE_String_Const devname, unsigned int device);
215141cc406Sopenharmony_ci
216141cc406Sopenharmony_cistatic void
217141cc406Sopenharmony_ciprint_params(const SANE_Parameters params)
218141cc406Sopenharmony_ci{
219141cc406Sopenharmony_ci	DBG(6, "params.format          = %d\n", params.format);
220141cc406Sopenharmony_ci	DBG(6, "params.last_frame      = %d\n", params.last_frame);
221141cc406Sopenharmony_ci	DBG(6, "params.bytes_per_line  = %d\n", params.bytes_per_line);
222141cc406Sopenharmony_ci	DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line);
223141cc406Sopenharmony_ci	DBG(6, "params.lines           = %d\n", params.lines);
224141cc406Sopenharmony_ci	DBG(6, "params.depth           = %d\n", params.depth);
225141cc406Sopenharmony_ci}
226141cc406Sopenharmony_ci
227141cc406Sopenharmony_ci
228141cc406Sopenharmony_ci
229141cc406Sopenharmony_ci/****************************************************************************
230141cc406Sopenharmony_ci *   Low-level Network communication functions
231141cc406Sopenharmony_ci ****************************************************************************/
232141cc406Sopenharmony_ci
233141cc406Sopenharmony_ci
234141cc406Sopenharmony_ci#define MAGICOLOR_SNMP_SYSDESCR_OID  ".1.3.6.1.2.1.1.1.0"
235141cc406Sopenharmony_ci#define MAGICOLOR_SNMP_SYSOBJECT_OID ".1.3.6.1.2.1.1.2.0"
236141cc406Sopenharmony_ci#define MAGICOLOR_SNMP_MAC_OID       ".1.3.6.1.2.1.2.2.1.6.1"
237141cc406Sopenharmony_ci#define MAGICOLOR_SNMP_DEVICE_TREE   ".1.3.6.1.4.1.18334.1.1.1.1.1"
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_ci
240141cc406Sopenharmony_ci/* We don't have a packet wrapper, which holds packet size etc., so we
241141cc406Sopenharmony_ci   don't have to use a *read_raw and a *_read function... */
242141cc406Sopenharmony_cistatic int
243141cc406Sopenharmony_cisanei_magicolor_net_read(struct Magicolor_Scanner *s, unsigned char *buf, size_t wanted,
244141cc406Sopenharmony_ci		       SANE_Status * status)
245141cc406Sopenharmony_ci{
246141cc406Sopenharmony_ci	size_t size, read = 0;
247141cc406Sopenharmony_ci	struct pollfd fds[1];
248141cc406Sopenharmony_ci
249141cc406Sopenharmony_ci	*status = SANE_STATUS_GOOD;
250141cc406Sopenharmony_ci
251141cc406Sopenharmony_ci	/* poll for data-to-be-read (using a 5 seconds timeout) */
252141cc406Sopenharmony_ci	fds[0].fd = s->fd;
253141cc406Sopenharmony_ci	fds[0].events = POLLIN;
254141cc406Sopenharmony_ci	if (poll (fds, 1, MC_Request_Timeout) <= 0) {
255141cc406Sopenharmony_ci		*status = SANE_STATUS_IO_ERROR;
256141cc406Sopenharmony_ci		return read;
257141cc406Sopenharmony_ci	}
258141cc406Sopenharmony_ci
259141cc406Sopenharmony_ci	while (read < wanted) {
260141cc406Sopenharmony_ci		size = sanei_tcp_read(s->fd, buf + read, wanted - read);
261141cc406Sopenharmony_ci
262141cc406Sopenharmony_ci		if (size == 0)
263141cc406Sopenharmony_ci			break;
264141cc406Sopenharmony_ci
265141cc406Sopenharmony_ci		read += size;
266141cc406Sopenharmony_ci	}
267141cc406Sopenharmony_ci
268141cc406Sopenharmony_ci	if (read < wanted)
269141cc406Sopenharmony_ci		*status = SANE_STATUS_IO_ERROR;
270141cc406Sopenharmony_ci
271141cc406Sopenharmony_ci	return read;
272141cc406Sopenharmony_ci}
273141cc406Sopenharmony_ci
274141cc406Sopenharmony_ci/* We need to optionally pad the buffer with 0x00 to send 64-byte chunks.
275141cc406Sopenharmony_ci   On the other hand, the 0x04 commands don't need this, so we need two
276141cc406Sopenharmony_ci   functions, one *_write function that pads the buffer and then calls
277141cc406Sopenharmony_ci    *_write_raw */
278141cc406Sopenharmony_cistatic int
279141cc406Sopenharmony_cisanei_magicolor_net_write_raw(struct Magicolor_Scanner *s,
280141cc406Sopenharmony_ci			      const unsigned char *buf, size_t buf_size,
281141cc406Sopenharmony_ci			      SANE_Status *status)
282141cc406Sopenharmony_ci{
283141cc406Sopenharmony_ci	sanei_tcp_write(s->fd, buf, buf_size);
284141cc406Sopenharmony_ci	/* TODO: Check whether sending failed... */
285141cc406Sopenharmony_ci
286141cc406Sopenharmony_ci	*status = SANE_STATUS_GOOD;
287141cc406Sopenharmony_ci	return buf_size;
288141cc406Sopenharmony_ci}
289141cc406Sopenharmony_ci
290141cc406Sopenharmony_cistatic int
291141cc406Sopenharmony_cisanei_magicolor_net_write(struct Magicolor_Scanner *s,
292141cc406Sopenharmony_ci			  const unsigned char *buf, size_t buf_size,
293141cc406Sopenharmony_ci			  SANE_Status *status)
294141cc406Sopenharmony_ci{
295141cc406Sopenharmony_ci	size_t len = 64;
296141cc406Sopenharmony_ci	unsigned char *new_buf = malloc(len);
297141cc406Sopenharmony_ci	if (!new_buf) {
298141cc406Sopenharmony_ci		*status = SANE_STATUS_NO_MEM;
299141cc406Sopenharmony_ci		return 0;
300141cc406Sopenharmony_ci	}
301141cc406Sopenharmony_ci	memset(new_buf, 0x00, len);
302141cc406Sopenharmony_ci	if (buf_size > len)
303141cc406Sopenharmony_ci		buf_size = len;
304141cc406Sopenharmony_ci	if (buf_size)
305141cc406Sopenharmony_ci		memcpy(new_buf, buf, buf_size);
306141cc406Sopenharmony_ci	return sanei_magicolor_net_write_raw (s, new_buf, len, status);
307141cc406Sopenharmony_ci}
308141cc406Sopenharmony_ci
309141cc406Sopenharmony_cistatic SANE_Status
310141cc406Sopenharmony_cisanei_magicolor_net_open(struct Magicolor_Scanner *s)
311141cc406Sopenharmony_ci{
312141cc406Sopenharmony_ci	SANE_Status status;
313141cc406Sopenharmony_ci	unsigned char buf[5];
314141cc406Sopenharmony_ci
315141cc406Sopenharmony_ci	ssize_t read;
316141cc406Sopenharmony_ci	struct timeval tv;
317141cc406Sopenharmony_ci	struct MagicolorCmd *cmd = s->hw->cmd;
318141cc406Sopenharmony_ci
319141cc406Sopenharmony_ci	tv.tv_sec = 5;
320141cc406Sopenharmony_ci	tv.tv_usec = 0;
321141cc406Sopenharmony_ci
322141cc406Sopenharmony_ci	setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,  sizeof(tv));
323141cc406Sopenharmony_ci
324141cc406Sopenharmony_ci	DBG(1, "%s\n", __func__);
325141cc406Sopenharmony_ci
326141cc406Sopenharmony_ci	/* the scanner sends a kind of welcome msg */
327141cc406Sopenharmony_ci	read = sanei_magicolor_net_read(s, buf, 3, &status);
328141cc406Sopenharmony_ci	if (read != 3)
329141cc406Sopenharmony_ci		return SANE_STATUS_IO_ERROR;
330141cc406Sopenharmony_ci	if (buf[0] != cmd->net_wrapper_cmd || buf[1] != cmd->net_welcome) {
331141cc406Sopenharmony_ci		DBG (32, "Invalid welcome message received, Expected 0x%02x %02x 00, but got 0x%02x %02x %02x\n",
332141cc406Sopenharmony_ci			cmd->net_wrapper_cmd, cmd->net_welcome, buf[0], buf[1], buf[2]);
333141cc406Sopenharmony_ci		return SANE_STATUS_IO_ERROR;
334141cc406Sopenharmony_ci	} else if (buf[2] != 0x00) {
335141cc406Sopenharmony_ci		/* TODO: Handle response "04 00 01", indicating an error! */
336141cc406Sopenharmony_ci		DBG (32, "Welcome message received, busy status %02x\n", buf[2]);
337141cc406Sopenharmony_ci		/* TODO: Return a human-readable error message (Unable to connect to scanner, scanner is not ready) */
338141cc406Sopenharmony_ci		return SANE_STATUS_DEVICE_BUSY;
339141cc406Sopenharmony_ci	}
340141cc406Sopenharmony_ci
341141cc406Sopenharmony_ci	buf[0] = cmd->net_wrapper_cmd;
342141cc406Sopenharmony_ci	buf[1] = cmd->net_lock;
343141cc406Sopenharmony_ci	buf[2] = 0x00;
344141cc406Sopenharmony_ci	/* Copy the device's USB id to bytes 3-4: */
345141cc406Sopenharmony_ci	buf[3] = s->hw->cap->id & 0xff;
346141cc406Sopenharmony_ci	buf[4] = (s->hw->cap->id >> 8) & 0xff;
347141cc406Sopenharmony_ci
348141cc406Sopenharmony_ci	DBG(32, "Proper welcome message received, locking the scanner...\n");
349141cc406Sopenharmony_ci	sanei_magicolor_net_write_raw(s, buf, 5, &status);
350141cc406Sopenharmony_ci
351141cc406Sopenharmony_ci	read = sanei_magicolor_net_read(s, buf, 3, &status);
352141cc406Sopenharmony_ci	if (read != 3)
353141cc406Sopenharmony_ci		return SANE_STATUS_IO_ERROR;
354141cc406Sopenharmony_ci	if (buf[0] != cmd->net_wrapper_cmd || buf[1] != cmd->net_lock_ack || buf[2] != 0x00) {
355141cc406Sopenharmony_ci		DBG (32, "Welcome message received, Expected 0x%x %x 00, but got 0x%x %x %x\n",
356141cc406Sopenharmony_ci			cmd->net_wrapper_cmd, cmd->net_lock_ack, buf[0], buf[1], buf[2]);
357141cc406Sopenharmony_ci		return SANE_STATUS_IO_ERROR;
358141cc406Sopenharmony_ci	}
359141cc406Sopenharmony_ci
360141cc406Sopenharmony_ci	DBG(32, "scanner locked\n");
361141cc406Sopenharmony_ci
362141cc406Sopenharmony_ci	return status;
363141cc406Sopenharmony_ci}
364141cc406Sopenharmony_ci
365141cc406Sopenharmony_cistatic SANE_Status
366141cc406Sopenharmony_cisanei_magicolor_net_close(struct Magicolor_Scanner *s)
367141cc406Sopenharmony_ci{
368141cc406Sopenharmony_ci	SANE_Status status;
369141cc406Sopenharmony_ci	struct MagicolorCmd *cmd = s->hw->cmd;
370141cc406Sopenharmony_ci	unsigned char buf[3];
371141cc406Sopenharmony_ci
372141cc406Sopenharmony_ci	DBG(1, "%s\n", __func__);
373141cc406Sopenharmony_ci	buf[0] = cmd->net_wrapper_cmd;
374141cc406Sopenharmony_ci	buf[1] = cmd->net_unlock;
375141cc406Sopenharmony_ci	buf[2] = 0x00;
376141cc406Sopenharmony_ci	sanei_magicolor_net_write_raw(s, buf, 3, &status);
377141cc406Sopenharmony_ci	return status;
378141cc406Sopenharmony_ci}
379141cc406Sopenharmony_ci
380141cc406Sopenharmony_ci
381141cc406Sopenharmony_ci
382141cc406Sopenharmony_ci/****************************************************************************
383141cc406Sopenharmony_ci *   Low-level USB communication functions
384141cc406Sopenharmony_ci ****************************************************************************/
385141cc406Sopenharmony_ci
386141cc406Sopenharmony_ci#define SANE_MAGICOLOR_VENDOR_ID	(0x132b)
387141cc406Sopenharmony_ci
388141cc406Sopenharmony_ciSANE_Word sanei_magicolor_usb_product_ids[] = {
389141cc406Sopenharmony_ci  0x2089, /* magicolor 1690MF */
390141cc406Sopenharmony_ci  0x2079, /* magicolor 4690MF */
391141cc406Sopenharmony_ci  0				/* last entry - this is used for devices that are specified
392141cc406Sopenharmony_ci				   in the config file as "usb <vendor> <product>" */
393141cc406Sopenharmony_ci};
394141cc406Sopenharmony_ci
395141cc406Sopenharmony_cistatic int
396141cc406Sopenharmony_cisanei_magicolor_getNumberOfUSBProductIds (void)
397141cc406Sopenharmony_ci{
398141cc406Sopenharmony_ci  return sizeof (sanei_magicolor_usb_product_ids) / sizeof (SANE_Word);
399141cc406Sopenharmony_ci}
400141cc406Sopenharmony_ci
401141cc406Sopenharmony_ci
402141cc406Sopenharmony_ci
403141cc406Sopenharmony_ci
404141cc406Sopenharmony_ci/****************************************************************************
405141cc406Sopenharmony_ci *   Magicolor low-level communication commands
406141cc406Sopenharmony_ci ****************************************************************************/
407141cc406Sopenharmony_ci
408141cc406Sopenharmony_cistatic void dump_hex_buffer_dense (int level, const unsigned char *buf, size_t buf_size)
409141cc406Sopenharmony_ci{
410141cc406Sopenharmony_ci	size_t k;
411141cc406Sopenharmony_ci	char msg[1024], fmt_buf[1024];
412141cc406Sopenharmony_ci	memset (&msg[0], 0x00, 1024);
413141cc406Sopenharmony_ci	memset (&fmt_buf[0], 0x00, 1024);
414141cc406Sopenharmony_ci	for (k = 0; k < min(buf_size, 80); k++) {
415141cc406Sopenharmony_ci		if (k % 16 == 0) {
416141cc406Sopenharmony_ci			if (k>0) {
417141cc406Sopenharmony_ci				DBG (level, "%s\n", msg);
418141cc406Sopenharmony_ci				memset (&msg[0], 0x00, 1024);
419141cc406Sopenharmony_ci			}
420141cc406Sopenharmony_ci			sprintf (fmt_buf, "     0x%04lx  ", (unsigned long)k);
421141cc406Sopenharmony_ci			strcat (msg, fmt_buf);
422141cc406Sopenharmony_ci		}
423141cc406Sopenharmony_ci		if (k % 8 == 0) {
424141cc406Sopenharmony_ci			strcat (msg, " ");
425141cc406Sopenharmony_ci		}
426141cc406Sopenharmony_ci		sprintf (fmt_buf, " %02x" , buf[k]);
427141cc406Sopenharmony_ci		strcat (msg, fmt_buf);
428141cc406Sopenharmony_ci	}
429141cc406Sopenharmony_ci	if (msg[0] != 0 ) {
430141cc406Sopenharmony_ci		DBG (level, "%s\n", msg);
431141cc406Sopenharmony_ci	}
432141cc406Sopenharmony_ci}
433141cc406Sopenharmony_ci
434141cc406Sopenharmony_ci/* Create buffers containing the command and arguments. Length of reserved
435141cc406Sopenharmony_ci * buffer is returned. It's the caller's job to free the buffer! */
436141cc406Sopenharmony_cistatic int mc_create_buffer (Magicolor_Scanner *s, unsigned char cmd_type, unsigned char cmd,
437141cc406Sopenharmony_ci		      unsigned char **buf, unsigned char* arg1, size_t len1,
438141cc406Sopenharmony_ci		      SANE_Status *status)
439141cc406Sopenharmony_ci{
440141cc406Sopenharmony_ci	unsigned char* b = NULL;
441141cc406Sopenharmony_ci	size_t buf_len = 2+4+len1+4;
442141cc406Sopenharmony_ci	NOT_USED (s);
443141cc406Sopenharmony_ci	if (len1 <= 0)
444141cc406Sopenharmony_ci		buf_len = 6; /* no args, just cmd + final 0x00 00 00 00 */
445141cc406Sopenharmony_ci	*buf = b = malloc (buf_len);
446141cc406Sopenharmony_ci	memset (b, 0x00, buf_len);
447141cc406Sopenharmony_ci	if (!b) {
448141cc406Sopenharmony_ci		*status = SANE_STATUS_NO_MEM;
449141cc406Sopenharmony_ci		return 0;
450141cc406Sopenharmony_ci	}
451141cc406Sopenharmony_ci	b[0] = cmd_type;
452141cc406Sopenharmony_ci	b[1] = cmd;
453141cc406Sopenharmony_ci	if (len1>0) {
454141cc406Sopenharmony_ci		b[2] = len1 & 0xff;
455141cc406Sopenharmony_ci		b[3] = (len1 >> 8) & 0xff;
456141cc406Sopenharmony_ci		b[4] = (len1 >> 16) & 0xff;
457141cc406Sopenharmony_ci		b[5] = (len1 >> 24) & 0xff;
458141cc406Sopenharmony_ci		if (arg1)
459141cc406Sopenharmony_ci			memcpy(b+6, arg1, len1);
460141cc406Sopenharmony_ci	}
461141cc406Sopenharmony_ci	/* Writing the final 0x00 00 00 00 is not necessary, they are 0x00 already */
462141cc406Sopenharmony_ci	*status = SANE_STATUS_GOOD;
463141cc406Sopenharmony_ci	return buf_len;
464141cc406Sopenharmony_ci}
465141cc406Sopenharmony_ci
466141cc406Sopenharmony_cistatic int mc_create_buffer2 (Magicolor_Scanner *s, unsigned char cmd_type, unsigned char cmd,
467141cc406Sopenharmony_ci		       unsigned char **buf, unsigned char* arg1, size_t len1,
468141cc406Sopenharmony_ci		       unsigned char* arg2, size_t len2, SANE_Status *status)
469141cc406Sopenharmony_ci{
470141cc406Sopenharmony_ci	unsigned char* b = NULL;
471141cc406Sopenharmony_ci	size_t buf_len = 2+4+len1+4+len2+4;
472141cc406Sopenharmony_ci	/* If any of the two args has size 0, use the simpler mc_create_buffer */
473141cc406Sopenharmony_ci	if (len1<=0)
474141cc406Sopenharmony_ci		return mc_create_buffer (s, cmd_type, cmd, buf, arg2, len2, status);
475141cc406Sopenharmony_ci	else if (len2<=0)
476141cc406Sopenharmony_ci		return mc_create_buffer (s, cmd_type, cmd, buf, arg1, len1, status);
477141cc406Sopenharmony_ci	/* Allocate memory and copy over args and their lengths */
478141cc406Sopenharmony_ci	*buf = b = malloc (buf_len);
479141cc406Sopenharmony_ci	if (!b) {
480141cc406Sopenharmony_ci		*status = SANE_STATUS_NO_MEM;
481141cc406Sopenharmony_ci		return 0;
482141cc406Sopenharmony_ci	}
483141cc406Sopenharmony_ci	memset (b, 0x00, buf_len);
484141cc406Sopenharmony_ci	b[0] = cmd_type;
485141cc406Sopenharmony_ci	b[1] = cmd;
486141cc406Sopenharmony_ci	/* copy over the argument length in lower endian */
487141cc406Sopenharmony_ci	b[2] = len1 & 0xff;
488141cc406Sopenharmony_ci	b[3] = (len1 >> 8) & 0xff;
489141cc406Sopenharmony_ci	b[4] = (len1 >> 16) & 0xff;
490141cc406Sopenharmony_ci	b[5] = (len1 >> 24) & 0xff;
491141cc406Sopenharmony_ci	if (arg1) {
492141cc406Sopenharmony_ci		/* Copy the arguments */
493141cc406Sopenharmony_ci		memcpy(b+6, arg1, len1);
494141cc406Sopenharmony_ci	}
495141cc406Sopenharmony_ci	/* copy over the second argument length in little endian */
496141cc406Sopenharmony_ci	b[6+len1] = len2 & 0xff;
497141cc406Sopenharmony_ci	b[7+len1] = (len2 >> 8) & 0xff;
498141cc406Sopenharmony_ci	b[8+len1] = (len2 >> 16) & 0xff;
499141cc406Sopenharmony_ci	b[9+len1] = (len2 >> 24) & 0xff;
500141cc406Sopenharmony_ci	if (arg2) {
501141cc406Sopenharmony_ci		memcpy(b+10+len1, arg2, len2);
502141cc406Sopenharmony_ci	}
503141cc406Sopenharmony_ci	*status = SANE_STATUS_GOOD;
504141cc406Sopenharmony_ci	return buf_len;
505141cc406Sopenharmony_ci}
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_cistatic int
508141cc406Sopenharmony_cimc_send(Magicolor_Scanner * s, void *buf, size_t buf_size, SANE_Status * status)
509141cc406Sopenharmony_ci{
510141cc406Sopenharmony_ci	DBG(15, "%s: size = %lu\n", __func__, (u_long) buf_size);
511141cc406Sopenharmony_ci
512141cc406Sopenharmony_ci	if (DBG_LEVEL >= 125) {
513141cc406Sopenharmony_ci		const unsigned char *s = buf;
514141cc406Sopenharmony_ci		DBG(125, "Cmd: 0x%02x %02x, complete buffer:\n", s[0], s[1]);
515141cc406Sopenharmony_ci		dump_hex_buffer_dense (125, s, buf_size);
516141cc406Sopenharmony_ci	}
517141cc406Sopenharmony_ci
518141cc406Sopenharmony_ci	if (s->hw->connection == SANE_MAGICOLOR_NET) {
519141cc406Sopenharmony_ci		return sanei_magicolor_net_write(s, buf, buf_size, status);
520141cc406Sopenharmony_ci	} else if (s->hw->connection == SANE_MAGICOLOR_USB) {
521141cc406Sopenharmony_ci		size_t n;
522141cc406Sopenharmony_ci		n = buf_size;
523141cc406Sopenharmony_ci		*status = sanei_usb_write_bulk(s->fd, buf, &n);
524141cc406Sopenharmony_ci		DBG(125, "USB: wrote %lu bytes, status: %s\n", (unsigned long)n, sane_strstatus(*status));
525141cc406Sopenharmony_ci		return n;
526141cc406Sopenharmony_ci	}
527141cc406Sopenharmony_ci
528141cc406Sopenharmony_ci	*status = SANE_STATUS_INVAL;
529141cc406Sopenharmony_ci	return 0;
530141cc406Sopenharmony_ci	/* never reached */
531141cc406Sopenharmony_ci}
532141cc406Sopenharmony_ci
533141cc406Sopenharmony_cistatic ssize_t
534141cc406Sopenharmony_cimc_recv(Magicolor_Scanner * s, void *buf, ssize_t buf_size,
535141cc406Sopenharmony_ci	    SANE_Status * status)
536141cc406Sopenharmony_ci{
537141cc406Sopenharmony_ci	ssize_t n = 0;
538141cc406Sopenharmony_ci
539141cc406Sopenharmony_ci	DBG(15, "%s: size = %ld, buf = %p\n", __func__, (long) buf_size, buf);
540141cc406Sopenharmony_ci
541141cc406Sopenharmony_ci	if (s->hw->connection == SANE_MAGICOLOR_NET) {
542141cc406Sopenharmony_ci		n = sanei_magicolor_net_read(s, buf, buf_size, status);
543141cc406Sopenharmony_ci	} else if (s->hw->connection == SANE_MAGICOLOR_USB) {
544141cc406Sopenharmony_ci		/* !!! only report an error if we don't read anything */
545141cc406Sopenharmony_ci		n = buf_size;	/* buf_size gets overwritten */
546141cc406Sopenharmony_ci		*status =
547141cc406Sopenharmony_ci			sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf,
548141cc406Sopenharmony_ci					    (size_t *) & n);
549141cc406Sopenharmony_ci
550141cc406Sopenharmony_ci		if (n > 0)
551141cc406Sopenharmony_ci			*status = SANE_STATUS_GOOD;
552141cc406Sopenharmony_ci	}
553141cc406Sopenharmony_ci
554141cc406Sopenharmony_ci	if (n < buf_size) {
555141cc406Sopenharmony_ci		DBG(1, "%s: expected = %lu, got = %ld\n", __func__,
556141cc406Sopenharmony_ci		    (u_long) buf_size, (long) n);
557141cc406Sopenharmony_ci		*status = SANE_STATUS_IO_ERROR;
558141cc406Sopenharmony_ci	}
559141cc406Sopenharmony_ci
560141cc406Sopenharmony_ci	/* dump buffer if appropriate */
561141cc406Sopenharmony_ci	if (DBG_LEVEL >= 127 && n > 0) {
562141cc406Sopenharmony_ci		const unsigned char* b=buf;
563141cc406Sopenharmony_ci		dump_hex_buffer_dense (125, b, buf_size);
564141cc406Sopenharmony_ci	}
565141cc406Sopenharmony_ci
566141cc406Sopenharmony_ci	return n;
567141cc406Sopenharmony_ci}
568141cc406Sopenharmony_ci
569141cc406Sopenharmony_ci/* Simple function to exchange a fixed amount of
570141cc406Sopenharmony_ci * data with the scanner
571141cc406Sopenharmony_ci */
572141cc406Sopenharmony_cistatic SANE_Status
573141cc406Sopenharmony_cimc_txrx(Magicolor_Scanner * s, unsigned char *txbuf, size_t txlen,
574141cc406Sopenharmony_ci	    unsigned char *rxbuf, size_t rxlen)
575141cc406Sopenharmony_ci{
576141cc406Sopenharmony_ci	SANE_Status status;
577141cc406Sopenharmony_ci
578141cc406Sopenharmony_ci	mc_send(s, txbuf, txlen, &status);
579141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
580141cc406Sopenharmony_ci		DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status));
581141cc406Sopenharmony_ci		return status;
582141cc406Sopenharmony_ci	}
583141cc406Sopenharmony_ci
584141cc406Sopenharmony_ci	mc_recv(s, rxbuf, rxlen, &status);
585141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
586141cc406Sopenharmony_ci		DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status));
587141cc406Sopenharmony_ci	}
588141cc406Sopenharmony_ci
589141cc406Sopenharmony_ci	return status;
590141cc406Sopenharmony_ci}
591141cc406Sopenharmony_ci
592141cc406Sopenharmony_ci
593141cc406Sopenharmony_ci
594141cc406Sopenharmony_ci
595141cc406Sopenharmony_ci
596141cc406Sopenharmony_ci/****************************************************************************
597141cc406Sopenharmony_ci *   Magicolor high-level communication commands
598141cc406Sopenharmony_ci ****************************************************************************/
599141cc406Sopenharmony_ci
600141cc406Sopenharmony_ci
601141cc406Sopenharmony_ci/** 0x03 09 01 - Request last error
602141cc406Sopenharmony_ci * <- Information block (0x00 for OK, 0x01 for ERROR)
603141cc406Sopenharmony_ci */
604141cc406Sopenharmony_cistatic SANE_Status
605141cc406Sopenharmony_cicmd_request_error (SANE_Handle handle)
606141cc406Sopenharmony_ci{
607141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
608141cc406Sopenharmony_ci	SANE_Status status;
609141cc406Sopenharmony_ci	unsigned char params[1];
610141cc406Sopenharmony_ci	unsigned char *buf;
611141cc406Sopenharmony_ci	size_t buflen;
612141cc406Sopenharmony_ci
613141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
614141cc406Sopenharmony_ci
615141cc406Sopenharmony_ci	if (s->hw->cmd->request_status == 0)
616141cc406Sopenharmony_ci		return SANE_STATUS_UNSUPPORTED;
617141cc406Sopenharmony_ci
618141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_error,
619141cc406Sopenharmony_ci				   &buf, NULL, 1, &status);
620141cc406Sopenharmony_ci	if (buflen <= 0 ) {
621141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
622141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
623141cc406Sopenharmony_ci		return status;
624141cc406Sopenharmony_ci	}
625141cc406Sopenharmony_ci
626141cc406Sopenharmony_ci	status = mc_txrx (s, buf, buflen, params, 1);
627141cc406Sopenharmony_ci	free(buf);
628141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
629141cc406Sopenharmony_ci		return status;
630141cc406Sopenharmony_ci
631141cc406Sopenharmony_ci	DBG(1, "status: %02x\n", params[0]);
632141cc406Sopenharmony_ci
633141cc406Sopenharmony_ci	switch (params[0]) {
634141cc406Sopenharmony_ci		case STATUS_READY:
635141cc406Sopenharmony_ci			DBG(1, " ready\n");
636141cc406Sopenharmony_ci			break;
637141cc406Sopenharmony_ci		case STATUS_ADF_JAM:
638141cc406Sopenharmony_ci			DBG(1, " paper jam in ADF\n");
639141cc406Sopenharmony_ci			return SANE_STATUS_JAMMED;
640141cc406Sopenharmony_ci			break;
641141cc406Sopenharmony_ci		case STATUS_OPEN:
642141cc406Sopenharmony_ci			DBG(1, " printer door open or waiting for button press\n");
643141cc406Sopenharmony_ci			return SANE_STATUS_COVER_OPEN;
644141cc406Sopenharmony_ci			break;
645141cc406Sopenharmony_ci		case STATUS_NOT_READY:
646141cc406Sopenharmony_ci			DBG(1, " scanner not ready (in use on another interface or warming up)\n");
647141cc406Sopenharmony_ci			return SANE_STATUS_DEVICE_BUSY;
648141cc406Sopenharmony_ci			break;
649141cc406Sopenharmony_ci		default:
650141cc406Sopenharmony_ci			DBG(1, " unknown status 0x%x\n", params[0]);
651141cc406Sopenharmony_ci	}
652141cc406Sopenharmony_ci	return status;
653141cc406Sopenharmony_ci}
654141cc406Sopenharmony_ci
655141cc406Sopenharmony_ci/** 0x03 0d  - Request status command */
656141cc406Sopenharmony_cistatic SANE_Status
657141cc406Sopenharmony_cicmd_request_status(SANE_Handle handle, unsigned char *b)
658141cc406Sopenharmony_ci{
659141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
660141cc406Sopenharmony_ci	SANE_Status status;
661141cc406Sopenharmony_ci	unsigned char *buf;
662141cc406Sopenharmony_ci	size_t buflen;
663141cc406Sopenharmony_ci
664141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
665141cc406Sopenharmony_ci	if (!b) {
666141cc406Sopenharmony_ci		DBG(1, "%s called with NULL buffer\n", __func__);
667141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
668141cc406Sopenharmony_ci	}
669141cc406Sopenharmony_ci	memset (b, 0x00, 0x0b); /* initialize all 0x0b bytes with 0 */
670141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_status,
671141cc406Sopenharmony_ci				   &buf, NULL, 0x0b, &status);
672141cc406Sopenharmony_ci	if (buflen <= 0 ) {
673141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
674141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
675141cc406Sopenharmony_ci		return status;
676141cc406Sopenharmony_ci	}
677141cc406Sopenharmony_ci
678141cc406Sopenharmony_ci	status = mc_txrx (s, buf, buflen, b, 0x0b);
679141cc406Sopenharmony_ci	free (buf);
680141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
681141cc406Sopenharmony_ci		DBG(8, "%s: Status NOT successfully retrieved\n", __func__);
682141cc406Sopenharmony_ci	else {
683141cc406Sopenharmony_ci		DBG(8, "%s: Status successfully retrieved:\n", __func__);
684141cc406Sopenharmony_ci		/* TODO: debug output of the returned parameters... */
685141cc406Sopenharmony_ci		DBG (11, "  ADF status: 0x%02x", b[1]);
686141cc406Sopenharmony_ci		if (b[1] & ADF_LOADED) {
687141cc406Sopenharmony_ci			DBG (11, " loaded\n");
688141cc406Sopenharmony_ci		} else {
689141cc406Sopenharmony_ci			DBG (11, " not loaded\n");
690141cc406Sopenharmony_ci		}
691141cc406Sopenharmony_ci	}
692141cc406Sopenharmony_ci	return status;
693141cc406Sopenharmony_ci}
694141cc406Sopenharmony_ci
695141cc406Sopenharmony_ci
696141cc406Sopenharmony_ci/** 0x03 08  - Start scan command */
697141cc406Sopenharmony_cistatic SANE_Status
698141cc406Sopenharmony_cicmd_start_scan (SANE_Handle handle, size_t value)
699141cc406Sopenharmony_ci{
700141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
701141cc406Sopenharmony_ci	SANE_Status status;
702141cc406Sopenharmony_ci	unsigned char params1[4], params2[1];
703141cc406Sopenharmony_ci	unsigned char *buf;
704141cc406Sopenharmony_ci	size_t buflen;
705141cc406Sopenharmony_ci
706141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
707141cc406Sopenharmony_ci	/* Copy params to buffers */
708141cc406Sopenharmony_ci	/* arg1 is expected returned bytes per line, 4-byte little endian */
709141cc406Sopenharmony_ci	/* arg2 is unknown, seems to be always 0x00 */
710141cc406Sopenharmony_ci	params1[0] = value & 0xff;
711141cc406Sopenharmony_ci	params1[1] = (value >> 8) & 0xff;
712141cc406Sopenharmony_ci	params1[2] = (value >> 16) & 0xff;
713141cc406Sopenharmony_ci	params1[3] = (value >> 24) & 0xff;
714141cc406Sopenharmony_ci
715141cc406Sopenharmony_ci	params2[0] = 0x00;
716141cc406Sopenharmony_ci	buflen = mc_create_buffer2 (s, s->hw->cmd->scanner_cmd, s->hw->cmd->start_scanning,
717141cc406Sopenharmony_ci				    &buf, params1, 4, params2, 1, &status);
718141cc406Sopenharmony_ci	if (buflen <= 0 ) {
719141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
720141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
721141cc406Sopenharmony_ci		return status;
722141cc406Sopenharmony_ci	}
723141cc406Sopenharmony_ci
724141cc406Sopenharmony_ci	mc_send(s, buf, buflen, &status);
725141cc406Sopenharmony_ci	free (buf);
726141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
727141cc406Sopenharmony_ci		DBG(8, "%s: Data NOT successfully sent\n", __func__);
728141cc406Sopenharmony_ci	else
729141cc406Sopenharmony_ci		DBG(8, "%s: Data successfully sent\n", __func__);
730141cc406Sopenharmony_ci	return status;
731141cc406Sopenharmony_ci}
732141cc406Sopenharmony_ci
733141cc406Sopenharmony_ci/** 0x03 0a  - Cancel(?) Scan command */
734141cc406Sopenharmony_ci/* TODO: Does this command really mean CANCEL??? */
735141cc406Sopenharmony_cistatic SANE_Status
736141cc406Sopenharmony_cicmd_cancel_scan (SANE_Handle handle)
737141cc406Sopenharmony_ci{
738141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
739141cc406Sopenharmony_ci	SANE_Status status;
740141cc406Sopenharmony_ci	unsigned char *buf;
741141cc406Sopenharmony_ci	size_t buflen;
742141cc406Sopenharmony_ci
743141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
744141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->stop_scanning,
745141cc406Sopenharmony_ci				    &buf, NULL, 0, &status);
746141cc406Sopenharmony_ci	if (buflen <= 0 ) {
747141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
748141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
749141cc406Sopenharmony_ci		return status;
750141cc406Sopenharmony_ci	}
751141cc406Sopenharmony_ci
752141cc406Sopenharmony_ci	mc_send(s, buf, buflen, &status);
753141cc406Sopenharmony_ci	free (buf);
754141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
755141cc406Sopenharmony_ci		DBG(8, "%s: Data NOT successfully sent\n", __func__);
756141cc406Sopenharmony_ci	else
757141cc406Sopenharmony_ci		DBG(8, "%s: Data successfully sent\n", __func__);
758141cc406Sopenharmony_ci	return status;
759141cc406Sopenharmony_ci}
760141cc406Sopenharmony_ci
761141cc406Sopenharmony_ci/** 0x03 12  - Finish(?) scan command */
762141cc406Sopenharmony_ci/* TODO: Does this command really mean FINISH??? */
763141cc406Sopenharmony_cistatic SANE_Status
764141cc406Sopenharmony_cicmd_finish_scan (SANE_Handle handle)
765141cc406Sopenharmony_ci{
766141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
767141cc406Sopenharmony_ci	SANE_Status status;
768141cc406Sopenharmony_ci	unsigned char *buf, returned[0x0b];
769141cc406Sopenharmony_ci	size_t buflen;
770141cc406Sopenharmony_ci
771141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
772141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->unknown2,
773141cc406Sopenharmony_ci				    &buf, NULL, 0x0b, &status);
774141cc406Sopenharmony_ci	if (buflen <= 0 ) {
775141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
776141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
777141cc406Sopenharmony_ci		return status;
778141cc406Sopenharmony_ci	}
779141cc406Sopenharmony_ci	memset (&returned[0], 0x00, 0x0b);
780141cc406Sopenharmony_ci
781141cc406Sopenharmony_ci	status = mc_txrx (s, buf, buflen, returned, 0x0b);
782141cc406Sopenharmony_ci	free (buf);
783141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
784141cc406Sopenharmony_ci		DBG(8, "%s: Data NOT successfully sent\n", __func__);
785141cc406Sopenharmony_ci	else
786141cc406Sopenharmony_ci		DBG(8, "%s: Data successfully sent\n", __func__);
787141cc406Sopenharmony_ci	return status;
788141cc406Sopenharmony_ci}
789141cc406Sopenharmony_ci
790141cc406Sopenharmony_ci/** 0x03 0b  - Get scanning parameters command
791141cc406Sopenharmony_ci *    input buffer seems to be 0x00 always */
792141cc406Sopenharmony_cistatic SANE_Status
793141cc406Sopenharmony_cicmd_get_scanning_parameters(SANE_Handle handle,
794141cc406Sopenharmony_ci			    SANE_Frame *format, SANE_Int *depth,
795141cc406Sopenharmony_ci			    SANE_Int *data_pixels, SANE_Int *pixels_per_line,
796141cc406Sopenharmony_ci			    SANE_Int *lines)
797141cc406Sopenharmony_ci{
798141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
799141cc406Sopenharmony_ci	SANE_Status status;
800141cc406Sopenharmony_ci	unsigned char *txbuf, rxbuf[8];
801141cc406Sopenharmony_ci	size_t buflen;
802141cc406Sopenharmony_ci	NOT_USED (format);
803141cc406Sopenharmony_ci	NOT_USED (depth);
804141cc406Sopenharmony_ci
805141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
806141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd,
807141cc406Sopenharmony_ci				   s->hw->cmd->request_scan_parameters,
808141cc406Sopenharmony_ci				   &txbuf, NULL, 8, &status);
809141cc406Sopenharmony_ci	if (buflen <= 0 ) {
810141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
811141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
812141cc406Sopenharmony_ci		return status;
813141cc406Sopenharmony_ci	}
814141cc406Sopenharmony_ci
815141cc406Sopenharmony_ci	status = mc_txrx (s, txbuf, buflen, rxbuf, 8);
816141cc406Sopenharmony_ci	free (txbuf);
817141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
818141cc406Sopenharmony_ci		DBG(8, "%s: Parameters NOT successfully retrieved\n", __func__);
819141cc406Sopenharmony_ci	else {
820141cc406Sopenharmony_ci		DBG(8, "%s: Parameters successfully retrieved\n", __func__);
821141cc406Sopenharmony_ci
822141cc406Sopenharmony_ci		/* Assign px_per_line and lines. Bytes 7-8 must match 3-4 */
823141cc406Sopenharmony_ci		if (rxbuf[2]!=rxbuf[6] || rxbuf[3]!=rxbuf[7]) {
824141cc406Sopenharmony_ci			DBG (1, "%s: ERROR: Returned image parameters indicate an "
825141cc406Sopenharmony_ci				"unsupported device: Bytes 3-4 do not match "
826141cc406Sopenharmony_ci				"bytes 7-8! Trying to continue with bytes 3-4.\n",
827141cc406Sopenharmony_ci				__func__);
828141cc406Sopenharmony_ci			dump_hex_buffer_dense (1, rxbuf, 8);
829141cc406Sopenharmony_ci		}
830141cc406Sopenharmony_ci		/* Read returned values, encoded in 2-byte little endian */
831141cc406Sopenharmony_ci		*data_pixels = rxbuf[1] * 0x100 + rxbuf[0];
832141cc406Sopenharmony_ci		*lines = rxbuf[3] * 0x100 + rxbuf[2];
833141cc406Sopenharmony_ci		*pixels_per_line = rxbuf[5] * 0x100 + rxbuf[4];
834141cc406Sopenharmony_ci		DBG (8, "%s: data_pixels = 0x%x (%u), lines = 0x%x (%u), "
835141cc406Sopenharmony_ci		        "pixels_per_line = 0x%x (%u)\n", __func__,
836141cc406Sopenharmony_ci		        *data_pixels, *data_pixels,
837141cc406Sopenharmony_ci		        *lines, *lines,
838141cc406Sopenharmony_ci		        *pixels_per_line, *pixels_per_line);
839141cc406Sopenharmony_ci
840141cc406Sopenharmony_ci		/*
841141cc406Sopenharmony_ci		 * SECURITY REMEDIATION
842141cc406Sopenharmony_ci		 * See gitlab issue #279 - Issue10 SIGFPE in mc_setup_block_mode
843141cc406Sopenharmony_ci		 *
844141cc406Sopenharmony_ci		 * pixels_per_line cannot be zero, otherwise a division by zero error can occur later.
845141cc406Sopenharmony_ci		 * Added checking the parameters to ensure that this issue cannot occur.
846141cc406Sopenharmony_ci		 *
847141cc406Sopenharmony_ci		 * Additionally to the reported issue, it makes no sense for any of the values of
848141cc406Sopenharmony_ci		 * data_pixels, lines or pixels_per_line to be zero. I do not have any knowledge
849141cc406Sopenharmony_ci		 * of valid maximums for these parameters.
850141cc406Sopenharmony_ci		 *
851141cc406Sopenharmony_ci		 */
852141cc406Sopenharmony_ci		if ((*data_pixels == 0) || (*lines == 0) || (*pixels_per_line == 0)) {
853141cc406Sopenharmony_ci			DBG (1, "%s: ERROR: Returned image parameters contain invalid "
854141cc406Sopenharmony_ci				"bytes. Zero values for these parameters are not rational.\n",
855141cc406Sopenharmony_ci				__func__);
856141cc406Sopenharmony_ci			dump_hex_buffer_dense (1, rxbuf, 8);
857141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
858141cc406Sopenharmony_ci		}
859141cc406Sopenharmony_ci	}
860141cc406Sopenharmony_ci
861141cc406Sopenharmony_ci	return status;
862141cc406Sopenharmony_ci}
863141cc406Sopenharmony_ci
864141cc406Sopenharmony_ci/** 0x03 0c  - Set scanning parameters command */
865141cc406Sopenharmony_cistatic SANE_Status
866141cc406Sopenharmony_cicmd_set_scanning_parameters(SANE_Handle handle,
867141cc406Sopenharmony_ci	unsigned char resolution, unsigned char color_mode,
868141cc406Sopenharmony_ci	unsigned char brightness, unsigned char contrast,
869141cc406Sopenharmony_ci	int tl_x, int tl_y, int width, int height, unsigned char source)
870141cc406Sopenharmony_ci{
871141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
872141cc406Sopenharmony_ci	SANE_Status status;
873141cc406Sopenharmony_ci	unsigned char param[0x11];
874141cc406Sopenharmony_ci	unsigned char *buf;
875141cc406Sopenharmony_ci	size_t buflen;
876141cc406Sopenharmony_ci
877141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
878141cc406Sopenharmony_ci	/* Copy over the params to the param byte array */
879141cc406Sopenharmony_ci	/* Byte structure:
880141cc406Sopenharmony_ci	 *   byte 0:     resolution
881141cc406Sopenharmony_ci	 *   byte 1:     color mode
882141cc406Sopenharmony_ci	 *   byte 2:     brightness
883141cc406Sopenharmony_ci	 *   byte 3:     0xff
884141cc406Sopenharmony_ci	 *   byte 4-5:   x-start
885141cc406Sopenharmony_ci	 *   byte 6-7:   y-start
886141cc406Sopenharmony_ci	 *   byte 8-9:  x-extent
887141cc406Sopenharmony_ci	 *   byte 10-11: y-extent
888141cc406Sopenharmony_ci	 *   byte 12:    source (ADF/FBF)
889141cc406Sopenharmony_ci	 **/
890141cc406Sopenharmony_ci	memset (&param[0], 0x00, 0x11);
891141cc406Sopenharmony_ci	param[0] = resolution;
892141cc406Sopenharmony_ci	param[1] = color_mode;
893141cc406Sopenharmony_ci	param[2] = brightness;
894141cc406Sopenharmony_ci	param[3] = contrast | 0xff; /* TODO: Always 0xff? What about contrast? */
895141cc406Sopenharmony_ci	/* Image coordinates are encoded 2-byte little endian: */
896141cc406Sopenharmony_ci	param[4] = tl_x & 0xff;
897141cc406Sopenharmony_ci	param[5] = (tl_x >> 8) & 0xff;
898141cc406Sopenharmony_ci	param[6] = tl_y & 0xff;
899141cc406Sopenharmony_ci	param[7] = (tl_y >> 8) & 0xff;
900141cc406Sopenharmony_ci	param[8] = width & 0xff;
901141cc406Sopenharmony_ci	param[9] = (width >> 8) & 0xff;
902141cc406Sopenharmony_ci	param[10] = height & 0xff;
903141cc406Sopenharmony_ci	param[11] = (height >> 8) & 0xff;
904141cc406Sopenharmony_ci
905141cc406Sopenharmony_ci	param[12] = source;
906141cc406Sopenharmony_ci
907141cc406Sopenharmony_ci	/* dump buffer if appropriate */
908141cc406Sopenharmony_ci	DBG (127, "  Scanning parameter buffer:");
909141cc406Sopenharmony_ci	dump_hex_buffer_dense (127, param, 0x11);
910141cc406Sopenharmony_ci
911141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->set_scan_parameters,
912141cc406Sopenharmony_ci				   &buf, param, 0x11, &status);
913141cc406Sopenharmony_ci	if (buflen <= 0 ) {
914141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
915141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
916141cc406Sopenharmony_ci		return status;
917141cc406Sopenharmony_ci	}
918141cc406Sopenharmony_ci
919141cc406Sopenharmony_ci	mc_send(s, buf, buflen, &status);
920141cc406Sopenharmony_ci	free (buf);
921141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
922141cc406Sopenharmony_ci		DBG(8, "%s: Data NOT successfully sent\n", __func__);
923141cc406Sopenharmony_ci	else
924141cc406Sopenharmony_ci		DBG(8, "%s: Data successfully sent\n", __func__);
925141cc406Sopenharmony_ci	return status;
926141cc406Sopenharmony_ci}
927141cc406Sopenharmony_ci
928141cc406Sopenharmony_ci/** 0x03 ??  - Request push button status command */
929141cc406Sopenharmony_ci#if 0
930141cc406Sopenharmony_cistatic SANE_Status
931141cc406Sopenharmony_cicmd_request_push_button_status(SANE_Handle handle, unsigned char *bstatus)
932141cc406Sopenharmony_ci{
933141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
934141cc406Sopenharmony_ci	SANE_Status status;
935141cc406Sopenharmony_ci	unsigned char *buf;
936141cc406Sopenharmony_ci	size_t buflen;
937141cc406Sopenharmony_ci
938141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
939141cc406Sopenharmony_ci
940141cc406Sopenharmony_ci
941141cc406Sopenharmony_ci	if (s->hw->cmd->unknown1 == 0)
942141cc406Sopenharmony_ci		return SANE_STATUS_UNSUPPORTED;
943141cc406Sopenharmony_ci
944141cc406Sopenharmony_ci	DBG(8, "%s: Supported\n", __func__);
945141cc406Sopenharmony_ci	memset (bstatus, 0x00, 1);
946141cc406Sopenharmony_ci	buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->unknown1,
947141cc406Sopenharmony_ci				   &buf, bstatus, 1, &status);
948141cc406Sopenharmony_ci	if (buflen <= 0 ) {
949141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
950141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
951141cc406Sopenharmony_ci		return status;
952141cc406Sopenharmony_ci	}
953141cc406Sopenharmony_ci
954141cc406Sopenharmony_ci	status = mc_txrx (s, buf, buflen, bstatus, 1);
955141cc406Sopenharmony_ci	free(buf);
956141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
957141cc406Sopenharmony_ci		return status;
958141cc406Sopenharmony_ci
959141cc406Sopenharmony_ci	DBG(1, "push button status: %02x ", bstatus[0]);
960141cc406Sopenharmony_ci	switch (bstatus[0]) {
961141cc406Sopenharmony_ci		/* TODO: What's the response code for button pressed??? */
962141cc406Sopenharmony_ci		default:
963141cc406Sopenharmony_ci			DBG(1, " unknown\n");
964141cc406Sopenharmony_ci			status = SANE_STATUS_UNSUPPORTED;
965141cc406Sopenharmony_ci	}
966141cc406Sopenharmony_ci	return status;
967141cc406Sopenharmony_ci}
968141cc406Sopenharmony_ci#endif
969141cc406Sopenharmony_ci
970141cc406Sopenharmony_ci/** 0x03 0e  - Read data command */
971141cc406Sopenharmony_cistatic SANE_Status
972141cc406Sopenharmony_cicmd_read_data (SANE_Handle handle, unsigned char *buf, size_t len)
973141cc406Sopenharmony_ci{
974141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
975141cc406Sopenharmony_ci	SANE_Status status;
976141cc406Sopenharmony_ci	unsigned char *txbuf;
977141cc406Sopenharmony_ci	unsigned char param[4];
978141cc406Sopenharmony_ci	size_t txbuflen;
979141cc406Sopenharmony_ci	int oldtimeout = MC_Request_Timeout;
980141cc406Sopenharmony_ci
981141cc406Sopenharmony_ci	DBG(8, "%s\n", __func__);
982141cc406Sopenharmony_ci	param[0] = len & 0xff;
983141cc406Sopenharmony_ci	param[1] = (len >> 8) & 0xff;
984141cc406Sopenharmony_ci	param[2] = (len >> 16) & 0xff;
985141cc406Sopenharmony_ci	param[3] = (len >> 24) & 0xff;
986141cc406Sopenharmony_ci
987141cc406Sopenharmony_ci	txbuflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_data,
988141cc406Sopenharmony_ci				   &txbuf, param, 4, &status);
989141cc406Sopenharmony_ci	if (txbuflen <= 0 ) {
990141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
991141cc406Sopenharmony_ci	} else if (status != SANE_STATUS_GOOD) {
992141cc406Sopenharmony_ci		return status;
993141cc406Sopenharmony_ci	}
994141cc406Sopenharmony_ci
995141cc406Sopenharmony_ci	/* Temporarily set the poll timeout to 10 seconds instead of 2,
996141cc406Sopenharmony_ci	 * because a color scan needs >5 seconds to initialize. */
997141cc406Sopenharmony_ci	MC_Request_Timeout = MC_Scan_Data_Timeout;
998141cc406Sopenharmony_ci	status = mc_txrx (s, txbuf, txbuflen, buf, len);
999141cc406Sopenharmony_ci	MC_Request_Timeout = oldtimeout;
1000141cc406Sopenharmony_ci	free (txbuf);
1001141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1002141cc406Sopenharmony_ci		DBG(8, "%s: Image data NOT successfully retrieved\n", __func__);
1003141cc406Sopenharmony_ci	else {
1004141cc406Sopenharmony_ci		DBG(8, "%s: Image data successfully retrieved\n", __func__);
1005141cc406Sopenharmony_ci	}
1006141cc406Sopenharmony_ci
1007141cc406Sopenharmony_ci	return status;
1008141cc406Sopenharmony_ci}
1009141cc406Sopenharmony_ci
1010141cc406Sopenharmony_ci
1011141cc406Sopenharmony_ci
1012141cc406Sopenharmony_ci/* TODO: 0x03 0f command (unknown), 0x03 10 command (set button wait) */
1013141cc406Sopenharmony_ci
1014141cc406Sopenharmony_ci
1015141cc406Sopenharmony_ci
1016141cc406Sopenharmony_ci
1017141cc406Sopenharmony_ci/****************************************************************************
1018141cc406Sopenharmony_ci *   Magicolor backend high-level operations
1019141cc406Sopenharmony_ci ****************************************************************************/
1020141cc406Sopenharmony_ci
1021141cc406Sopenharmony_ci
1022141cc406Sopenharmony_cistatic void
1023141cc406Sopenharmony_cimc_dev_init(Magicolor_Device *dev, const char *devname, int conntype)
1024141cc406Sopenharmony_ci{
1025141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
1026141cc406Sopenharmony_ci
1027141cc406Sopenharmony_ci	dev->name = NULL;
1028141cc406Sopenharmony_ci	dev->model = NULL;
1029141cc406Sopenharmony_ci	dev->connection = conntype;
1030141cc406Sopenharmony_ci	dev->sane.name = devname;
1031141cc406Sopenharmony_ci	dev->sane.model = NULL;
1032141cc406Sopenharmony_ci	dev->sane.type = "flatbed scanner";
1033141cc406Sopenharmony_ci	dev->sane.vendor = "Magicolor";
1034141cc406Sopenharmony_ci	dev->cap = &magicolor_cap[MAGICOLOR_CAP_DEFAULT];
1035141cc406Sopenharmony_ci	dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_DEFAULT];
1036141cc406Sopenharmony_ci	/* Change default level when using a network connection */
1037141cc406Sopenharmony_ci	if (dev->connection == SANE_MAGICOLOR_NET)
1038141cc406Sopenharmony_ci		dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_NET];
1039141cc406Sopenharmony_ci}
1040141cc406Sopenharmony_ci
1041141cc406Sopenharmony_cistatic SANE_Status
1042141cc406Sopenharmony_cimc_dev_post_init(struct Magicolor_Device *dev)
1043141cc406Sopenharmony_ci{
1044141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
1045141cc406Sopenharmony_ci	NOT_USED (dev);
1046141cc406Sopenharmony_ci	/* Correct device parameters if needed */
1047141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1048141cc406Sopenharmony_ci}
1049141cc406Sopenharmony_ci
1050141cc406Sopenharmony_cistatic SANE_Status
1051141cc406Sopenharmony_cimc_set_model(Magicolor_Scanner * s, const char *model, size_t len)
1052141cc406Sopenharmony_ci{
1053141cc406Sopenharmony_ci	unsigned char *buf;
1054141cc406Sopenharmony_ci	unsigned char *p;
1055141cc406Sopenharmony_ci	struct Magicolor_Device *dev = s->hw;
1056141cc406Sopenharmony_ci
1057141cc406Sopenharmony_ci	buf = malloc(len + 1);
1058141cc406Sopenharmony_ci	if (buf == NULL)
1059141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
1060141cc406Sopenharmony_ci
1061141cc406Sopenharmony_ci	memcpy(buf, model, len);
1062141cc406Sopenharmony_ci	buf[len] = '\0';
1063141cc406Sopenharmony_ci
1064141cc406Sopenharmony_ci	p = &buf[len - 1];
1065141cc406Sopenharmony_ci
1066141cc406Sopenharmony_ci	while (*p == ' ') {
1067141cc406Sopenharmony_ci		*p = '\0';
1068141cc406Sopenharmony_ci		p--;
1069141cc406Sopenharmony_ci	}
1070141cc406Sopenharmony_ci
1071141cc406Sopenharmony_ci	if (dev->model)
1072141cc406Sopenharmony_ci		free(dev->model);
1073141cc406Sopenharmony_ci
1074141cc406Sopenharmony_ci	dev->model = strndup((const char *) buf, len);
1075141cc406Sopenharmony_ci	dev->sane.model = dev->model;
1076141cc406Sopenharmony_ci	DBG(10, "%s: model is '%s'\n", __func__, dev->model);
1077141cc406Sopenharmony_ci
1078141cc406Sopenharmony_ci	free(buf);
1079141cc406Sopenharmony_ci
1080141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1081141cc406Sopenharmony_ci}
1082141cc406Sopenharmony_ci
1083141cc406Sopenharmony_cistatic void
1084141cc406Sopenharmony_cimc_set_device (SANE_Handle handle, unsigned int device)
1085141cc406Sopenharmony_ci{
1086141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
1087141cc406Sopenharmony_ci	Magicolor_Device *dev = s->hw;
1088141cc406Sopenharmony_ci	const char* cmd_level;
1089141cc406Sopenharmony_ci	int n;
1090141cc406Sopenharmony_ci
1091141cc406Sopenharmony_ci	DBG(1, "%s: 0x%x\n", __func__, device);
1092141cc406Sopenharmony_ci
1093141cc406Sopenharmony_ci	for (n = 0; n < NELEMS (magicolor_cap); n++) {
1094141cc406Sopenharmony_ci		if (magicolor_cap[n].id == device)
1095141cc406Sopenharmony_ci			break;
1096141cc406Sopenharmony_ci	}
1097141cc406Sopenharmony_ci	if (n < NELEMS(magicolor_cap)) {
1098141cc406Sopenharmony_ci		dev->cap = &magicolor_cap[n];
1099141cc406Sopenharmony_ci	} else {
1100141cc406Sopenharmony_ci		dev->cap = &magicolor_cap[MAGICOLOR_CAP_DEFAULT];
1101141cc406Sopenharmony_ci		DBG(1, " unknown device 0x%x, using default %s\n",
1102141cc406Sopenharmony_ci		    device, dev->cap->model);
1103141cc406Sopenharmony_ci	}
1104141cc406Sopenharmony_ci	mc_set_model (s, dev->cap->model, strlen (dev->cap->model));
1105141cc406Sopenharmony_ci
1106141cc406Sopenharmony_ci	cmd_level = dev->cap->cmds;
1107141cc406Sopenharmony_ci	/* set command type and level */
1108141cc406Sopenharmony_ci	for (n = 0; n < NELEMS(magicolor_cmd); n++) {
1109141cc406Sopenharmony_ci		if (!strcmp(cmd_level, magicolor_cmd[n].level))
1110141cc406Sopenharmony_ci			break;
1111141cc406Sopenharmony_ci	}
1112141cc406Sopenharmony_ci
1113141cc406Sopenharmony_ci	if (n < NELEMS(magicolor_cmd)) {
1114141cc406Sopenharmony_ci		dev->cmd = &magicolor_cmd[n];
1115141cc406Sopenharmony_ci	} else {
1116141cc406Sopenharmony_ci		dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_DEFAULT];
1117141cc406Sopenharmony_ci		DBG(1, " unknown command level %s, using %s\n",
1118141cc406Sopenharmony_ci		    cmd_level, dev->cmd->level);
1119141cc406Sopenharmony_ci	}
1120141cc406Sopenharmony_ci}
1121141cc406Sopenharmony_ci
1122141cc406Sopenharmony_cistatic SANE_Status
1123141cc406Sopenharmony_cimc_discover_capabilities(Magicolor_Scanner *s)
1124141cc406Sopenharmony_ci{
1125141cc406Sopenharmony_ci	SANE_Status status;
1126141cc406Sopenharmony_ci	Magicolor_Device *dev = s->hw;
1127141cc406Sopenharmony_ci
1128141cc406Sopenharmony_ci	SANE_String_Const *source_list_add = source_list;
1129141cc406Sopenharmony_ci
1130141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
1131141cc406Sopenharmony_ci
1132141cc406Sopenharmony_ci	/* always add flatbed */
1133141cc406Sopenharmony_ci	*source_list_add++ = FBF_STR;
1134141cc406Sopenharmony_ci	/* TODO: How can I check for existence of an ADF??? */
1135141cc406Sopenharmony_ci	if (dev->cap->ADF)
1136141cc406Sopenharmony_ci		*source_list_add++ = ADF_STR;
1137141cc406Sopenharmony_ci
1138141cc406Sopenharmony_ci	/* TODO: Is there any capability that we can extract from the
1139141cc406Sopenharmony_ci	 *       device by some scanne command? So far, it looks like
1140141cc406Sopenharmony_ci	 *       the device does not support any reporting. I don't even
1141141cc406Sopenharmony_ci	 *       see a way to determine which device we are talking to!
1142141cc406Sopenharmony_ci	 */
1143141cc406Sopenharmony_ci
1144141cc406Sopenharmony_ci
1145141cc406Sopenharmony_ci	/* request error status */
1146141cc406Sopenharmony_ci	status = cmd_request_error(s);
1147141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1148141cc406Sopenharmony_ci		return status;
1149141cc406Sopenharmony_ci
1150141cc406Sopenharmony_ci	dev->x_range = &dev->cap->fbf_x_range;
1151141cc406Sopenharmony_ci	dev->y_range = &dev->cap->fbf_y_range;
1152141cc406Sopenharmony_ci
1153141cc406Sopenharmony_ci	DBG(5, "   x-range: %f %f\n", SANE_UNFIX(dev->x_range->min), SANE_UNFIX(dev->x_range->max));
1154141cc406Sopenharmony_ci	DBG(5, "   y-range: %f %f\n", SANE_UNFIX(dev->y_range->min), SANE_UNFIX(dev->y_range->max));
1155141cc406Sopenharmony_ci
1156141cc406Sopenharmony_ci	DBG(5, "End of %s, status:%s\n", __func__, sane_strstatus(status));
1157141cc406Sopenharmony_ci	*source_list_add = NULL; /* add end marker to source list */
1158141cc406Sopenharmony_ci	return status;
1159141cc406Sopenharmony_ci}
1160141cc406Sopenharmony_ci
1161141cc406Sopenharmony_cistatic SANE_Status
1162141cc406Sopenharmony_cimc_setup_block_mode (Magicolor_Scanner *s)
1163141cc406Sopenharmony_ci{
1164141cc406Sopenharmony_ci	/* block_len should always be a multiple of bytes_per_line, so
1165141cc406Sopenharmony_ci	 * we retrieve only whole lines at once */
1166141cc406Sopenharmony_ci	s->block_len = (int)(0xff00/s->scan_bytes_per_line) * s->scan_bytes_per_line;
1167141cc406Sopenharmony_ci	s->blocks = s->data_len / s->block_len;
1168141cc406Sopenharmony_ci	s->last_len = s->data_len - (s->blocks * s->block_len);
1169141cc406Sopenharmony_ci	if (s->last_len>0)
1170141cc406Sopenharmony_ci		s->blocks++;
1171141cc406Sopenharmony_ci	DBG(5, "%s: block_len=0x%x, last_len=0x%0x, blocks=%d\n", __func__, s->block_len, s->last_len, s->blocks);
1172141cc406Sopenharmony_ci	s->counter = 0;
1173141cc406Sopenharmony_ci	s->bytes_read_in_line = 0;
1174141cc406Sopenharmony_ci	if (s->line_buffer)
1175141cc406Sopenharmony_ci		free(s->line_buffer);
1176141cc406Sopenharmony_ci	s->line_buffer = malloc(s->scan_bytes_per_line);
1177141cc406Sopenharmony_ci	if (s->line_buffer == NULL) {
1178141cc406Sopenharmony_ci		DBG(1, "out of memory (line %d)\n", __LINE__);
1179141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
1180141cc406Sopenharmony_ci	}
1181141cc406Sopenharmony_ci
1182141cc406Sopenharmony_ci
1183141cc406Sopenharmony_ci	DBG (5, " %s: Setup block mode - scan_bytes_per_line=%d, pixels_per_line=%d, depth=%d, data_len=%x, block_len=%x, blocks=%d, last_len=%x\n",
1184141cc406Sopenharmony_ci		__func__, s->scan_bytes_per_line, s->params.pixels_per_line, s->params.depth, s->data_len, s->block_len, s->blocks, s->last_len);
1185141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1186141cc406Sopenharmony_ci}
1187141cc406Sopenharmony_ci
1188141cc406Sopenharmony_ci/* Call the 0x03 0c command to set scanning parameters from the s->opt list */
1189141cc406Sopenharmony_cistatic SANE_Status
1190141cc406Sopenharmony_cimc_set_scanning_parameters(Magicolor_Scanner * s)
1191141cc406Sopenharmony_ci{
1192141cc406Sopenharmony_ci	SANE_Status status;
1193141cc406Sopenharmony_ci	unsigned char rs, source, brightness;
1194141cc406Sopenharmony_ci	struct mode_param *mparam = &mode_params[s->val[OPT_MODE].w];
1195141cc406Sopenharmony_ci	SANE_Int scan_pixels_per_line = 0;
1196141cc406Sopenharmony_ci
1197141cc406Sopenharmony_ci	DBG(1, "%s\n", __func__);
1198141cc406Sopenharmony_ci
1199141cc406Sopenharmony_ci	/* Find the resolution in the res list and assign the index to buf[1] */
1200141cc406Sopenharmony_ci	for (rs=0; rs < s->hw->cap->res_list_size; rs++ ) {
1201141cc406Sopenharmony_ci		if ( s->val[OPT_RESOLUTION].w == s->hw->cap->res_list[rs] )
1202141cc406Sopenharmony_ci			break;
1203141cc406Sopenharmony_ci	}
1204141cc406Sopenharmony_ci
1205141cc406Sopenharmony_ci	if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap)) {
1206141cc406Sopenharmony_ci		brightness = s->val[OPT_BRIGHTNESS].w;
1207141cc406Sopenharmony_ci	} else {
1208141cc406Sopenharmony_ci		brightness = 5;
1209141cc406Sopenharmony_ci	}
1210141cc406Sopenharmony_ci
1211141cc406Sopenharmony_ci	/* ADF used? */
1212141cc406Sopenharmony_ci	if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) {
1213141cc406Sopenharmony_ci		/* Use ADF */
1214141cc406Sopenharmony_ci		if (s->val[OPT_ADF_MODE].w == 0) {
1215141cc406Sopenharmony_ci			source = 0x01;
1216141cc406Sopenharmony_ci		} else {
1217141cc406Sopenharmony_ci			/* Use duplex */
1218141cc406Sopenharmony_ci			source = 0x02;
1219141cc406Sopenharmony_ci		}
1220141cc406Sopenharmony_ci	} else {
1221141cc406Sopenharmony_ci		source = 0x00;
1222141cc406Sopenharmony_ci	}
1223141cc406Sopenharmony_ci
1224141cc406Sopenharmony_ci	/* TODO: Any way to set PREVIEW??? */
1225141cc406Sopenharmony_ci
1226141cc406Sopenharmony_ci	/* Remaining bytes unused */
1227141cc406Sopenharmony_ci	status = cmd_set_scanning_parameters(s,
1228141cc406Sopenharmony_ci					     rs, mparam->flags,  /* res, color mode */
1229141cc406Sopenharmony_ci					     brightness, 0xff, /* brightness, contrast? */
1230141cc406Sopenharmony_ci					     s->left, s->top,  /* top/left start */
1231141cc406Sopenharmony_ci					     s->width, s->height, /* extent */
1232141cc406Sopenharmony_ci					     source); /* source */
1233141cc406Sopenharmony_ci
1234141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1235141cc406Sopenharmony_ci		DBG (2, "%s: Command cmd_set_scanning_parameters failed, %s\n",
1236141cc406Sopenharmony_ci		     __func__, sane_strstatus(status));
1237141cc406Sopenharmony_ci
1238141cc406Sopenharmony_ci	/* Now query the scanner for the current image parameters */
1239141cc406Sopenharmony_ci	status = cmd_get_scanning_parameters (s,
1240141cc406Sopenharmony_ci			&s->params.format, &s->params.depth,
1241141cc406Sopenharmony_ci			&scan_pixels_per_line,
1242141cc406Sopenharmony_ci			&s->params.pixels_per_line, &s->params.lines);
1243141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
1244141cc406Sopenharmony_ci		DBG (2, "%s: Command cmd_get_scanning_parameters failed, %s\n",
1245141cc406Sopenharmony_ci		     __func__, sane_strstatus(status));
1246141cc406Sopenharmony_ci		return status;
1247141cc406Sopenharmony_ci	}
1248141cc406Sopenharmony_ci
1249141cc406Sopenharmony_ci	/* Calculate how many bytes are really used per line */
1250141cc406Sopenharmony_ci	s->params.bytes_per_line = ceil (s->params.pixels_per_line * s->params.depth / 8.0);
1251141cc406Sopenharmony_ci	if (s->val[OPT_MODE].w == MODE_COLOR)
1252141cc406Sopenharmony_ci		s->params.bytes_per_line *= 3;
1253141cc406Sopenharmony_ci
1254141cc406Sopenharmony_ci	/* Calculate how many bytes per line will be returned by the scanner.
1255141cc406Sopenharmony_ci	 * The values needed for this are returned by get_scannign_parameters */
1256141cc406Sopenharmony_ci	s->scan_bytes_per_line = ceil (scan_pixels_per_line * s->params.depth / 8.0);
1257141cc406Sopenharmony_ci	if (s->val[OPT_MODE].w == MODE_COLOR) {
1258141cc406Sopenharmony_ci		s->scan_bytes_per_line *= 3;
1259141cc406Sopenharmony_ci	}
1260141cc406Sopenharmony_ci	s->data_len = s->params.lines * s->scan_bytes_per_line;
1261141cc406Sopenharmony_ci
1262141cc406Sopenharmony_ci	status = mc_setup_block_mode (s);
1263141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1264141cc406Sopenharmony_ci		DBG (2, "%s: Command mc_setup_block_mode failed, %s\n",
1265141cc406Sopenharmony_ci		     __func__, sane_strstatus(status));
1266141cc406Sopenharmony_ci
1267141cc406Sopenharmony_ci	DBG (1, "%s: bytes_read  in line: %d\n", __func__, s->bytes_read_in_line);
1268141cc406Sopenharmony_ci
1269141cc406Sopenharmony_ci	return status;
1270141cc406Sopenharmony_ci}
1271141cc406Sopenharmony_ci
1272141cc406Sopenharmony_cistatic SANE_Status
1273141cc406Sopenharmony_cimc_check_adf(Magicolor_Scanner * s)
1274141cc406Sopenharmony_ci{
1275141cc406Sopenharmony_ci	SANE_Status status;
1276141cc406Sopenharmony_ci	unsigned char buf[0x0b];
1277141cc406Sopenharmony_ci
1278141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
1279141cc406Sopenharmony_ci
1280141cc406Sopenharmony_ci	status = cmd_request_status(s, buf);
1281141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1282141cc406Sopenharmony_ci		return status;
1283141cc406Sopenharmony_ci
1284141cc406Sopenharmony_ci	if (!(buf[1] & ADF_LOADED))
1285141cc406Sopenharmony_ci		return SANE_STATUS_NO_DOCS;
1286141cc406Sopenharmony_ci
1287141cc406Sopenharmony_ci	/* TODO: Check for jam in ADF */
1288141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1289141cc406Sopenharmony_ci}
1290141cc406Sopenharmony_ci
1291141cc406Sopenharmony_cistatic SANE_Status
1292141cc406Sopenharmony_cimc_scan_finish(Magicolor_Scanner * s)
1293141cc406Sopenharmony_ci{
1294141cc406Sopenharmony_ci	SANE_Status status;
1295141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
1296141cc406Sopenharmony_ci
1297141cc406Sopenharmony_ci	/* If we have not yet read all data, cancel the scan */
1298141cc406Sopenharmony_ci	if (s->buf && !s->eof)
1299141cc406Sopenharmony_ci		status = cmd_cancel_scan (s);
1300141cc406Sopenharmony_ci
1301141cc406Sopenharmony_ci	if (s->line_buffer)
1302141cc406Sopenharmony_ci		free (s->line_buffer);
1303141cc406Sopenharmony_ci	s->line_buffer = NULL;
1304141cc406Sopenharmony_ci	free(s->buf);
1305141cc406Sopenharmony_ci	s->buf = s->end = s->ptr = NULL;
1306141cc406Sopenharmony_ci
1307141cc406Sopenharmony_ci	/* TODO: Any magicolor command for "scan finished"? */
1308141cc406Sopenharmony_ci	status = cmd_finish_scan (s);
1309141cc406Sopenharmony_ci
1310141cc406Sopenharmony_ci	status = cmd_request_error(s);
1311141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
1312141cc406Sopenharmony_ci		cmd_cancel_scan (s);
1313141cc406Sopenharmony_ci		return status;
1314141cc406Sopenharmony_ci        }
1315141cc406Sopenharmony_ci
1316141cc406Sopenharmony_ci	/* XXX required? */
1317141cc406Sopenharmony_ci	/* TODO:	cmd_reset(s);*/
1318141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1319141cc406Sopenharmony_ci}
1320141cc406Sopenharmony_ci
1321141cc406Sopenharmony_cistatic void
1322141cc406Sopenharmony_cimc_copy_image_data(Magicolor_Scanner * s, SANE_Byte * data, SANE_Int max_length,
1323141cc406Sopenharmony_ci		   SANE_Int * length)
1324141cc406Sopenharmony_ci{
1325141cc406Sopenharmony_ci	DBG (1, "%s: bytes_read  in line: %d\n", __func__, s->bytes_read_in_line);
1326141cc406Sopenharmony_ci	if (s->params.format == SANE_FRAME_RGB) {
1327141cc406Sopenharmony_ci		SANE_Int bytes_available, scan_pixels_per_line = s->scan_bytes_per_line/3;
1328141cc406Sopenharmony_ci		*length = 0;
1329141cc406Sopenharmony_ci
1330141cc406Sopenharmony_ci		while ((max_length >= s->params.bytes_per_line) && (s->ptr < s->end)) {
1331141cc406Sopenharmony_ci			SANE_Int bytes_to_copy = s->scan_bytes_per_line - s->bytes_read_in_line;
1332141cc406Sopenharmony_ci			/* First, fill the line buffer for the current line: */
1333141cc406Sopenharmony_ci			bytes_available = (s->end - s->ptr);
1334141cc406Sopenharmony_ci			/* Don't copy more than we have buffer and available */
1335141cc406Sopenharmony_ci			if (bytes_to_copy > bytes_available)
1336141cc406Sopenharmony_ci				bytes_to_copy = bytes_available;
1337141cc406Sopenharmony_ci
1338141cc406Sopenharmony_ci			if (bytes_to_copy > 0) {
1339141cc406Sopenharmony_ci				memcpy (s->line_buffer + s->bytes_read_in_line, s->ptr, bytes_to_copy);
1340141cc406Sopenharmony_ci				s->ptr += bytes_to_copy;
1341141cc406Sopenharmony_ci				s->bytes_read_in_line += bytes_to_copy;
1342141cc406Sopenharmony_ci			}
1343141cc406Sopenharmony_ci
1344141cc406Sopenharmony_ci			/* We have filled as much as possible of the current line
1345141cc406Sopenharmony_ci			 * with data from the scanner. If we have a complete line,
1346141cc406Sopenharmony_ci			 * copy it over. */
1347141cc406Sopenharmony_ci			if ((s->bytes_read_in_line >= s->scan_bytes_per_line) &&
1348141cc406Sopenharmony_ci			    (s->params.bytes_per_line <= max_length))
1349141cc406Sopenharmony_ci			{
1350141cc406Sopenharmony_ci				SANE_Int i;
1351141cc406Sopenharmony_ci				SANE_Byte *line = s->line_buffer;
1352141cc406Sopenharmony_ci				*length += s->params.bytes_per_line;
1353141cc406Sopenharmony_ci				for (i=0; i< s->params.pixels_per_line; ++i) {
1354141cc406Sopenharmony_ci					*data++ = line[0];
1355141cc406Sopenharmony_ci					*data++ = line[scan_pixels_per_line];
1356141cc406Sopenharmony_ci					*data++ = line[2 * scan_pixels_per_line];
1357141cc406Sopenharmony_ci					line++;
1358141cc406Sopenharmony_ci				}
1359141cc406Sopenharmony_ci				max_length -= s->params.bytes_per_line;
1360141cc406Sopenharmony_ci				s->bytes_read_in_line -= s->scan_bytes_per_line;
1361141cc406Sopenharmony_ci			}
1362141cc406Sopenharmony_ci		}
1363141cc406Sopenharmony_ci
1364141cc406Sopenharmony_ci	} else {
1365141cc406Sopenharmony_ci		/* B/W and Grayscale use the same structure, so we use the same code */
1366141cc406Sopenharmony_ci		SANE_Int bytes_available;
1367141cc406Sopenharmony_ci		*length = 0;
1368141cc406Sopenharmony_ci
1369141cc406Sopenharmony_ci		while ((max_length != 0) && (s->ptr < s->end)) {
1370141cc406Sopenharmony_ci			SANE_Int bytes_to_skip, bytes_to_copy;
1371141cc406Sopenharmony_ci			bytes_available = (s->end - s->ptr);
1372141cc406Sopenharmony_ci			bytes_to_copy = s->params.bytes_per_line - s->bytes_read_in_line;
1373141cc406Sopenharmony_ci			bytes_to_skip = s->scan_bytes_per_line - s->bytes_read_in_line;
1374141cc406Sopenharmony_ci
1375141cc406Sopenharmony_ci			/* Don't copy more than we have buffer */
1376141cc406Sopenharmony_ci			if (bytes_to_copy > max_length) {
1377141cc406Sopenharmony_ci				bytes_to_copy = max_length;
1378141cc406Sopenharmony_ci				bytes_to_skip = max_length;
1379141cc406Sopenharmony_ci			}
1380141cc406Sopenharmony_ci
1381141cc406Sopenharmony_ci			/* Don't copy/skip more bytes than we have read in */
1382141cc406Sopenharmony_ci			if (bytes_to_copy > bytes_available)
1383141cc406Sopenharmony_ci				bytes_to_copy = bytes_available;
1384141cc406Sopenharmony_ci			if (bytes_to_skip > bytes_available)
1385141cc406Sopenharmony_ci				bytes_to_skip = bytes_available;
1386141cc406Sopenharmony_ci
1387141cc406Sopenharmony_ci			if (bytes_to_copy > 0) {
1388141cc406Sopenharmony_ci				/* we have not yet copied all pixels of the line */
1389141cc406Sopenharmony_ci				memcpy (data, s->ptr, bytes_to_copy);
1390141cc406Sopenharmony_ci				max_length -= bytes_to_copy;
1391141cc406Sopenharmony_ci				*length += bytes_to_copy;
1392141cc406Sopenharmony_ci				data += bytes_to_copy;
1393141cc406Sopenharmony_ci			}
1394141cc406Sopenharmony_ci			if (bytes_to_skip > 0) {
1395141cc406Sopenharmony_ci				s->ptr += bytes_to_skip;
1396141cc406Sopenharmony_ci				s->bytes_read_in_line += bytes_to_skip;
1397141cc406Sopenharmony_ci			}
1398141cc406Sopenharmony_ci			if (s->bytes_read_in_line >= s->scan_bytes_per_line)
1399141cc406Sopenharmony_ci				s->bytes_read_in_line -= s->scan_bytes_per_line;
1400141cc406Sopenharmony_ci
1401141cc406Sopenharmony_ci		}
1402141cc406Sopenharmony_ci	}
1403141cc406Sopenharmony_ci}
1404141cc406Sopenharmony_ci
1405141cc406Sopenharmony_cistatic SANE_Status
1406141cc406Sopenharmony_cimc_init_parameters(Magicolor_Scanner * s)
1407141cc406Sopenharmony_ci{
1408141cc406Sopenharmony_ci	int dpi, optres;
1409141cc406Sopenharmony_ci
1410141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
1411141cc406Sopenharmony_ci
1412141cc406Sopenharmony_ci	memset(&s->params, 0, sizeof(SANE_Parameters));
1413141cc406Sopenharmony_ci
1414141cc406Sopenharmony_ci	dpi = s->val[OPT_RESOLUTION].w;
1415141cc406Sopenharmony_ci	optres = s->hw->cap->optical_res;
1416141cc406Sopenharmony_ci
1417141cc406Sopenharmony_ci	if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 ||
1418141cc406Sopenharmony_ci		SANE_UNFIX(s->val[OPT_BR_X].w) == 0)
1419141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1420141cc406Sopenharmony_ci
1421141cc406Sopenharmony_ci	/* TODO: Use OPT_RESOLUTION or fixed 600dpi for left/top/width/height? */
1422141cc406Sopenharmony_ci	s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5;
1423141cc406Sopenharmony_ci
1424141cc406Sopenharmony_ci	s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5;
1425141cc406Sopenharmony_ci
1426141cc406Sopenharmony_ci	s->width =
1427141cc406Sopenharmony_ci		((SANE_UNFIX(s->val[OPT_BR_X].w -
1428141cc406Sopenharmony_ci			   s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5;
1429141cc406Sopenharmony_ci
1430141cc406Sopenharmony_ci	s->height =
1431141cc406Sopenharmony_ci		((SANE_UNFIX(s->val[OPT_BR_Y].w -
1432141cc406Sopenharmony_ci			   s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5;
1433141cc406Sopenharmony_ci
1434141cc406Sopenharmony_ci	s->params.pixels_per_line = s->width * dpi / optres + 0.5;
1435141cc406Sopenharmony_ci	s->params.lines = s->height * dpi / optres + 0.5;
1436141cc406Sopenharmony_ci
1437141cc406Sopenharmony_ci
1438141cc406Sopenharmony_ci	DBG(1, "%s: resolution = %d, preview = %d\n",
1439141cc406Sopenharmony_ci		__func__, dpi, s->val[OPT_PREVIEW].w);
1440141cc406Sopenharmony_ci
1441141cc406Sopenharmony_ci	DBG(1, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n",
1442141cc406Sopenharmony_ci	    __func__, (void *) s, (void *) s->val,
1443141cc406Sopenharmony_ci	    SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w),
1444141cc406Sopenharmony_ci	    SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w));
1445141cc406Sopenharmony_ci
1446141cc406Sopenharmony_ci	/*
1447141cc406Sopenharmony_ci	 * The default color depth is stored in mode_params.depth:
1448141cc406Sopenharmony_ci	 */
1449141cc406Sopenharmony_ci	DBG(1, " %s, vor depth\n", __func__);
1450141cc406Sopenharmony_ci
1451141cc406Sopenharmony_ci	if (mode_params[s->val[OPT_MODE].w].depth == 1)
1452141cc406Sopenharmony_ci		s->params.depth = 1;
1453141cc406Sopenharmony_ci	else
1454141cc406Sopenharmony_ci		s->params.depth = s->val[OPT_BIT_DEPTH].w;
1455141cc406Sopenharmony_ci
1456141cc406Sopenharmony_ci	s->params.last_frame = SANE_TRUE;
1457141cc406Sopenharmony_ci
1458141cc406Sopenharmony_ci	s->params.bytes_per_line = ceil (s->params.depth * s->params.pixels_per_line / 8.0);
1459141cc406Sopenharmony_ci
1460141cc406Sopenharmony_ci	switch (s->val[OPT_MODE].w) {
1461141cc406Sopenharmony_ci	case MODE_BINARY:
1462141cc406Sopenharmony_ci	case MODE_GRAY:
1463141cc406Sopenharmony_ci		s->params.format = SANE_FRAME_GRAY;
1464141cc406Sopenharmony_ci		break;
1465141cc406Sopenharmony_ci	case MODE_COLOR:
1466141cc406Sopenharmony_ci		s->params.format = SANE_FRAME_RGB;
1467141cc406Sopenharmony_ci		s->params.bytes_per_line *= 3;
1468141cc406Sopenharmony_ci		break;
1469141cc406Sopenharmony_ci	}
1470141cc406Sopenharmony_ci
1471141cc406Sopenharmony_ci
1472141cc406Sopenharmony_ci	DBG(1, "%s: Parameters are format=%d, bytes_per_line=%d, lines=%d\n", __func__, s->params.format, s->params.bytes_per_line, s->params.lines);
1473141cc406Sopenharmony_ci	return (s->params.lines > 0) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL;
1474141cc406Sopenharmony_ci}
1475141cc406Sopenharmony_ci
1476141cc406Sopenharmony_cistatic SANE_Status
1477141cc406Sopenharmony_cimc_start_scan(Magicolor_Scanner * s)
1478141cc406Sopenharmony_ci{
1479141cc406Sopenharmony_ci	SANE_Status status = cmd_start_scan (s, s->data_len);
1480141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD ) {
1481141cc406Sopenharmony_ci		DBG (1, "%s: starting the scan failed (%s)\n", __func__, sane_strstatus(status));
1482141cc406Sopenharmony_ci	}
1483141cc406Sopenharmony_ci	return status;
1484141cc406Sopenharmony_ci}
1485141cc406Sopenharmony_ci
1486141cc406Sopenharmony_cistatic SANE_Status
1487141cc406Sopenharmony_cimc_read(struct Magicolor_Scanner *s)
1488141cc406Sopenharmony_ci{
1489141cc406Sopenharmony_ci	SANE_Status status = SANE_STATUS_GOOD;
1490141cc406Sopenharmony_ci	ssize_t buf_len = 0;
1491141cc406Sopenharmony_ci
1492141cc406Sopenharmony_ci	/* did we passed everything we read to sane? */
1493141cc406Sopenharmony_ci	if (s->ptr == s->end) {
1494141cc406Sopenharmony_ci
1495141cc406Sopenharmony_ci		if (s->eof)
1496141cc406Sopenharmony_ci			return SANE_STATUS_EOF;
1497141cc406Sopenharmony_ci
1498141cc406Sopenharmony_ci		s->counter++;
1499141cc406Sopenharmony_ci		buf_len = s->block_len;
1500141cc406Sopenharmony_ci
1501141cc406Sopenharmony_ci		if (s->counter == s->blocks && s->last_len)
1502141cc406Sopenharmony_ci			buf_len = s->last_len;
1503141cc406Sopenharmony_ci
1504141cc406Sopenharmony_ci		DBG(18, "%s: block %d/%d, size %lu\n", __func__,
1505141cc406Sopenharmony_ci			s->counter, s->blocks,
1506141cc406Sopenharmony_ci			(unsigned long) buf_len);
1507141cc406Sopenharmony_ci
1508141cc406Sopenharmony_ci		/* receive image data + error code */
1509141cc406Sopenharmony_ci		status = cmd_read_data (s, s->buf, buf_len);
1510141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD) {
1511141cc406Sopenharmony_ci			DBG (1, "%s: Receiving image data failed (%s)\n",
1512141cc406Sopenharmony_ci					__func__, sane_strstatus(status));
1513141cc406Sopenharmony_ci			cmd_cancel_scan(s);
1514141cc406Sopenharmony_ci			return status;
1515141cc406Sopenharmony_ci		}
1516141cc406Sopenharmony_ci
1517141cc406Sopenharmony_ci		DBG(18, "%s: successfully read %lu bytes\n", __func__, (unsigned long) buf_len);
1518141cc406Sopenharmony_ci
1519141cc406Sopenharmony_ci		if (s->counter < s->blocks) {
1520141cc406Sopenharmony_ci			if (s->canceling) {
1521141cc406Sopenharmony_ci				cmd_cancel_scan(s);
1522141cc406Sopenharmony_ci				return SANE_STATUS_CANCELLED;
1523141cc406Sopenharmony_ci			}
1524141cc406Sopenharmony_ci		} else
1525141cc406Sopenharmony_ci			s->eof = SANE_TRUE;
1526141cc406Sopenharmony_ci
1527141cc406Sopenharmony_ci		s->end = s->buf + buf_len;
1528141cc406Sopenharmony_ci		s->ptr = s->buf;
1529141cc406Sopenharmony_ci	}
1530141cc406Sopenharmony_ci
1531141cc406Sopenharmony_ci	return status;
1532141cc406Sopenharmony_ci}
1533141cc406Sopenharmony_ci
1534141cc406Sopenharmony_ci
1535141cc406Sopenharmony_ci
1536141cc406Sopenharmony_ci
1537141cc406Sopenharmony_ci/****************************************************************************
1538141cc406Sopenharmony_ci *   SANE API implementation (high-level functions)
1539141cc406Sopenharmony_ci ****************************************************************************/
1540141cc406Sopenharmony_ci
1541141cc406Sopenharmony_ci
1542141cc406Sopenharmony_ci#if HAVE_LIBSNMP
1543141cc406Sopenharmony_cistatic struct MagicolorCap *
1544141cc406Sopenharmony_cimc_get_device_from_identification (const char*ident)
1545141cc406Sopenharmony_ci{
1546141cc406Sopenharmony_ci	int n;
1547141cc406Sopenharmony_ci	for (n = 0; n < NELEMS (magicolor_cap); n++) {
1548141cc406Sopenharmony_ci		if ((strcmp (magicolor_cap[n].model, ident) == 0) || (strcmp (magicolor_cap[n].OID, ident) == 0))
1549141cc406Sopenharmony_ci		{
1550141cc406Sopenharmony_ci			return &magicolor_cap[n];
1551141cc406Sopenharmony_ci		}
1552141cc406Sopenharmony_ci	}
1553141cc406Sopenharmony_ci	return NULL;
1554141cc406Sopenharmony_ci}
1555141cc406Sopenharmony_ci#endif
1556141cc406Sopenharmony_ci
1557141cc406Sopenharmony_ci
1558141cc406Sopenharmony_ci/*
1559141cc406Sopenharmony_ci * close_scanner()
1560141cc406Sopenharmony_ci *
1561141cc406Sopenharmony_ci * Close the open scanner. Depending on the connection method, a different
1562141cc406Sopenharmony_ci * close function is called.
1563141cc406Sopenharmony_ci */
1564141cc406Sopenharmony_cistatic void
1565141cc406Sopenharmony_ciclose_scanner(Magicolor_Scanner *s)
1566141cc406Sopenharmony_ci{
1567141cc406Sopenharmony_ci	DBG(7, "%s: fd = %d\n", __func__, s->fd);
1568141cc406Sopenharmony_ci
1569141cc406Sopenharmony_ci	if (s->fd == -1)
1570141cc406Sopenharmony_ci		return;
1571141cc406Sopenharmony_ci
1572141cc406Sopenharmony_ci	mc_scan_finish(s);
1573141cc406Sopenharmony_ci	if (s->hw->connection == SANE_MAGICOLOR_NET) {
1574141cc406Sopenharmony_ci		sanei_magicolor_net_close(s);
1575141cc406Sopenharmony_ci		sanei_tcp_close(s->fd);
1576141cc406Sopenharmony_ci	} else if (s->hw->connection == SANE_MAGICOLOR_USB) {
1577141cc406Sopenharmony_ci		sanei_usb_close(s->fd);
1578141cc406Sopenharmony_ci	}
1579141cc406Sopenharmony_ci
1580141cc406Sopenharmony_ci	s->fd = -1;
1581141cc406Sopenharmony_ci}
1582141cc406Sopenharmony_ci
1583141cc406Sopenharmony_cistatic SANE_Bool
1584141cc406Sopenharmony_cisplit_scanner_name (const char *name, char * IP, unsigned int *model)
1585141cc406Sopenharmony_ci{
1586141cc406Sopenharmony_ci	const char *device = name;
1587141cc406Sopenharmony_ci	const char *qm;
1588141cc406Sopenharmony_ci	*model = 0;
1589141cc406Sopenharmony_ci	/* cut off leading net: */
1590141cc406Sopenharmony_ci	if (strncmp(device, "net:", 4) == 0)
1591141cc406Sopenharmony_ci		device = &device[4];
1592141cc406Sopenharmony_ci
1593141cc406Sopenharmony_ci	qm = strchr(device, '?');
1594141cc406Sopenharmony_ci	if (qm != NULL) {
1595141cc406Sopenharmony_ci		size_t len = qm-device;
1596141cc406Sopenharmony_ci		strncpy (IP, device, len);
1597141cc406Sopenharmony_ci		IP[len] = '\0';
1598141cc406Sopenharmony_ci		qm++;
1599141cc406Sopenharmony_ci		if (strncmp(qm, "model=", 6) == 0) {
1600141cc406Sopenharmony_ci			qm += 6;
1601141cc406Sopenharmony_ci			if (!sscanf(qm, "0x%x", model))
1602141cc406Sopenharmony_ci				sscanf(qm, "%x", model);
1603141cc406Sopenharmony_ci		}
1604141cc406Sopenharmony_ci	} else {
1605141cc406Sopenharmony_ci		strcpy (IP, device);
1606141cc406Sopenharmony_ci	}
1607141cc406Sopenharmony_ci	return SANE_TRUE;
1608141cc406Sopenharmony_ci}
1609141cc406Sopenharmony_ci
1610141cc406Sopenharmony_ci/*
1611141cc406Sopenharmony_ci * open_scanner()
1612141cc406Sopenharmony_ci *
1613141cc406Sopenharmony_ci * Open the scanner device. Depending on the connection method,
1614141cc406Sopenharmony_ci * different open functions are called.
1615141cc406Sopenharmony_ci */
1616141cc406Sopenharmony_ci
1617141cc406Sopenharmony_cistatic SANE_Status
1618141cc406Sopenharmony_ciopen_scanner(Magicolor_Scanner *s)
1619141cc406Sopenharmony_ci{
1620141cc406Sopenharmony_ci	SANE_Status status = 0;
1621141cc406Sopenharmony_ci
1622141cc406Sopenharmony_ci	DBG(7, "%s: %s\n", __func__, s->hw->sane.name);
1623141cc406Sopenharmony_ci
1624141cc406Sopenharmony_ci	if (s->fd != -1) {
1625141cc406Sopenharmony_ci		DBG(7, "scanner is already open: fd = %d\n", s->fd);
1626141cc406Sopenharmony_ci		return SANE_STATUS_GOOD;	/* no need to open the scanner */
1627141cc406Sopenharmony_ci	}
1628141cc406Sopenharmony_ci
1629141cc406Sopenharmony_ci	if (s->hw->connection == SANE_MAGICOLOR_NET) {
1630141cc406Sopenharmony_ci		/* device name has the form net:ipaddr?model=... */
1631141cc406Sopenharmony_ci		char IP[1024];
1632141cc406Sopenharmony_ci		unsigned int model = 0;
1633141cc406Sopenharmony_ci		if (!split_scanner_name (s->hw->sane.name, IP, &model))
1634141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
1635141cc406Sopenharmony_ci		status = sanei_tcp_open(IP, 4567, &s->fd);
1636141cc406Sopenharmony_ci		if (model>0)
1637141cc406Sopenharmony_ci			mc_set_device (s, model);
1638141cc406Sopenharmony_ci		if (status == SANE_STATUS_GOOD) {
1639141cc406Sopenharmony_ci			DBG(7, "awaiting welcome message\n");
1640141cc406Sopenharmony_ci			status = sanei_magicolor_net_open (s);
1641141cc406Sopenharmony_ci		}
1642141cc406Sopenharmony_ci
1643141cc406Sopenharmony_ci	} else if (s->hw->connection == SANE_MAGICOLOR_USB) {
1644141cc406Sopenharmony_ci		status = sanei_usb_open(s->hw->sane.name, &s->fd);
1645141cc406Sopenharmony_ci		if (s->hw->cap->out_ep>0)
1646141cc406Sopenharmony_ci			sanei_usb_set_endpoint (s->fd,
1647141cc406Sopenharmony_ci				USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, s->hw->cap->out_ep);
1648141cc406Sopenharmony_ci		if (s->hw->cap->in_ep>0)
1649141cc406Sopenharmony_ci			sanei_usb_set_endpoint (s->fd,
1650141cc406Sopenharmony_ci				USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, s->hw->cap->in_ep);
1651141cc406Sopenharmony_ci	}
1652141cc406Sopenharmony_ci
1653141cc406Sopenharmony_ci	if (status == SANE_STATUS_ACCESS_DENIED) {
1654141cc406Sopenharmony_ci		DBG(1, "please check that you have permissions on the device.\n");
1655141cc406Sopenharmony_ci		DBG(1, "if this is a multi-function device with a printer,\n");
1656141cc406Sopenharmony_ci		DBG(1, "disable any conflicting driver (like usblp).\n");
1657141cc406Sopenharmony_ci	}
1658141cc406Sopenharmony_ci
1659141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
1660141cc406Sopenharmony_ci		DBG(1, "%s open failed: %s\n", s->hw->sane.name,
1661141cc406Sopenharmony_ci			sane_strstatus(status));
1662141cc406Sopenharmony_ci	else
1663141cc406Sopenharmony_ci		DBG(3, "scanner opened\n");
1664141cc406Sopenharmony_ci
1665141cc406Sopenharmony_ci	return status;
1666141cc406Sopenharmony_ci}
1667141cc406Sopenharmony_ci
1668141cc406Sopenharmony_cistatic SANE_Status
1669141cc406Sopenharmony_cidetect_usb(struct Magicolor_Scanner *s)
1670141cc406Sopenharmony_ci{
1671141cc406Sopenharmony_ci	SANE_Status status;
1672141cc406Sopenharmony_ci	int vendor, product;
1673141cc406Sopenharmony_ci	int i, numIds;
1674141cc406Sopenharmony_ci	SANE_Bool is_valid;
1675141cc406Sopenharmony_ci
1676141cc406Sopenharmony_ci	/* if the sanei_usb_get_vendor_product call is not supported,
1677141cc406Sopenharmony_ci	 * then we just ignore this and rely on the user to config
1678141cc406Sopenharmony_ci	 * the correct device.
1679141cc406Sopenharmony_ci	 */
1680141cc406Sopenharmony_ci
1681141cc406Sopenharmony_ci	status = sanei_usb_get_vendor_product(s->fd, &vendor, &product);
1682141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
1683141cc406Sopenharmony_ci		DBG(1, "the device cannot be verified - will continue\n");
1684141cc406Sopenharmony_ci		return SANE_STATUS_GOOD;
1685141cc406Sopenharmony_ci	}
1686141cc406Sopenharmony_ci
1687141cc406Sopenharmony_ci	/* check the vendor ID to see if we are dealing with an MAGICOLOR device */
1688141cc406Sopenharmony_ci	if (vendor != SANE_MAGICOLOR_VENDOR_ID) {
1689141cc406Sopenharmony_ci		/* this is not a supported vendor ID */
1690141cc406Sopenharmony_ci		DBG(1, "not an Magicolor device at %s (vendor id=0x%x)\n",
1691141cc406Sopenharmony_ci		    s->hw->sane.name, vendor);
1692141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1693141cc406Sopenharmony_ci	}
1694141cc406Sopenharmony_ci
1695141cc406Sopenharmony_ci	numIds = sanei_magicolor_getNumberOfUSBProductIds();
1696141cc406Sopenharmony_ci	is_valid = SANE_FALSE;
1697141cc406Sopenharmony_ci	i = 0;
1698141cc406Sopenharmony_ci
1699141cc406Sopenharmony_ci	/* check all known product IDs to verify that we know
1700141cc406Sopenharmony_ci	 *	   about the device */
1701141cc406Sopenharmony_ci	while (i != numIds && !is_valid) {
1702141cc406Sopenharmony_ci		if (product == sanei_magicolor_usb_product_ids[i])
1703141cc406Sopenharmony_ci			is_valid = SANE_TRUE;
1704141cc406Sopenharmony_ci		i++;
1705141cc406Sopenharmony_ci	}
1706141cc406Sopenharmony_ci
1707141cc406Sopenharmony_ci	if (is_valid == SANE_FALSE) {
1708141cc406Sopenharmony_ci		DBG(1, "the device at %s is not a supported (product id=0x%x)\n",
1709141cc406Sopenharmony_ci		    s->hw->sane.name, product);
1710141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
1711141cc406Sopenharmony_ci	}
1712141cc406Sopenharmony_ci
1713141cc406Sopenharmony_ci	DBG(2, "found valid Magicolor scanner: 0x%x/0x%x (vendorID/productID)\n",
1714141cc406Sopenharmony_ci	    vendor, product);
1715141cc406Sopenharmony_ci	mc_set_device(s, product);
1716141cc406Sopenharmony_ci
1717141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1718141cc406Sopenharmony_ci}
1719141cc406Sopenharmony_ci
1720141cc406Sopenharmony_ci/*
1721141cc406Sopenharmony_ci * used by attach* and sane_get_devices
1722141cc406Sopenharmony_ci * a ptr to a single-linked list of Magicolor_Device structs
1723141cc406Sopenharmony_ci * a ptr to a null term array of ptrs to SANE_Device structs
1724141cc406Sopenharmony_ci */
1725141cc406Sopenharmony_cistatic int num_devices;		/* number of scanners attached to backend */
1726141cc406Sopenharmony_cistatic Magicolor_Device *first_dev;	/* first MAGICOLOR scanner in list */
1727141cc406Sopenharmony_cistatic const SANE_Device **devlist = NULL;
1728141cc406Sopenharmony_ci
1729141cc406Sopenharmony_cistatic struct Magicolor_Scanner *
1730141cc406Sopenharmony_ciscanner_create(struct Magicolor_Device *dev, SANE_Status *status)
1731141cc406Sopenharmony_ci{
1732141cc406Sopenharmony_ci	struct Magicolor_Scanner *s;
1733141cc406Sopenharmony_ci
1734141cc406Sopenharmony_ci	s = malloc(sizeof(struct Magicolor_Scanner));
1735141cc406Sopenharmony_ci	if (s == NULL) {
1736141cc406Sopenharmony_ci		*status = SANE_STATUS_NO_MEM;
1737141cc406Sopenharmony_ci		return NULL;
1738141cc406Sopenharmony_ci	}
1739141cc406Sopenharmony_ci
1740141cc406Sopenharmony_ci	memset(s, 0x00, sizeof(struct Magicolor_Scanner));
1741141cc406Sopenharmony_ci
1742141cc406Sopenharmony_ci	s->fd = -1;
1743141cc406Sopenharmony_ci	s->hw = dev;
1744141cc406Sopenharmony_ci
1745141cc406Sopenharmony_ci	return s;
1746141cc406Sopenharmony_ci}
1747141cc406Sopenharmony_ci
1748141cc406Sopenharmony_cistatic struct Magicolor_Scanner *
1749141cc406Sopenharmony_cidevice_detect(const char *name, int type, SANE_Status *status)
1750141cc406Sopenharmony_ci{
1751141cc406Sopenharmony_ci	struct Magicolor_Scanner *s;
1752141cc406Sopenharmony_ci	struct Magicolor_Device *dev;
1753141cc406Sopenharmony_ci
1754141cc406Sopenharmony_ci	/* try to find the device in our list */
1755141cc406Sopenharmony_ci	for (dev = first_dev; dev; dev = dev->next) {
1756141cc406Sopenharmony_ci		if (strcmp(dev->sane.name, name) == 0) {
1757141cc406Sopenharmony_ci			dev->missing = 0;
1758141cc406Sopenharmony_ci			DBG (10, "%s: Device %s already attached!\n", __func__,
1759141cc406Sopenharmony_ci			     name);
1760141cc406Sopenharmony_ci			return scanner_create(dev, status);
1761141cc406Sopenharmony_ci		}
1762141cc406Sopenharmony_ci	}
1763141cc406Sopenharmony_ci
1764141cc406Sopenharmony_ci	if (type == SANE_MAGICOLOR_NODEV) {
1765141cc406Sopenharmony_ci		*status = SANE_STATUS_INVAL;
1766141cc406Sopenharmony_ci		return NULL;
1767141cc406Sopenharmony_ci	}
1768141cc406Sopenharmony_ci
1769141cc406Sopenharmony_ci	/* alloc and clear our device structure */
1770141cc406Sopenharmony_ci	dev = malloc(sizeof(*dev));
1771141cc406Sopenharmony_ci	if (!dev) {
1772141cc406Sopenharmony_ci		*status = SANE_STATUS_NO_MEM;
1773141cc406Sopenharmony_ci		return NULL;
1774141cc406Sopenharmony_ci	}
1775141cc406Sopenharmony_ci	memset(dev, 0x00, sizeof(struct Magicolor_Device));
1776141cc406Sopenharmony_ci
1777141cc406Sopenharmony_ci	s = scanner_create(dev, status);
1778141cc406Sopenharmony_ci	if (s == NULL)
1779141cc406Sopenharmony_ci		return NULL;
1780141cc406Sopenharmony_ci
1781141cc406Sopenharmony_ci	mc_dev_init(dev, name, type);
1782141cc406Sopenharmony_ci
1783141cc406Sopenharmony_ci	*status = open_scanner(s);
1784141cc406Sopenharmony_ci	if (*status != SANE_STATUS_GOOD) {
1785141cc406Sopenharmony_ci		free(s);
1786141cc406Sopenharmony_ci		return NULL;
1787141cc406Sopenharmony_ci	}
1788141cc406Sopenharmony_ci
1789141cc406Sopenharmony_ci	/* from now on, close_scanner() must be called */
1790141cc406Sopenharmony_ci
1791141cc406Sopenharmony_ci	/* USB requires special care */
1792141cc406Sopenharmony_ci	if (dev->connection == SANE_MAGICOLOR_USB) {
1793141cc406Sopenharmony_ci		*status = detect_usb(s);
1794141cc406Sopenharmony_ci	}
1795141cc406Sopenharmony_ci
1796141cc406Sopenharmony_ci	if (*status != SANE_STATUS_GOOD)
1797141cc406Sopenharmony_ci		goto close;
1798141cc406Sopenharmony_ci
1799141cc406Sopenharmony_ci	/* set name and model (if not already set) */
1800141cc406Sopenharmony_ci	if (dev->model == NULL)
1801141cc406Sopenharmony_ci		mc_set_model(s, "generic", 7);
1802141cc406Sopenharmony_ci
1803141cc406Sopenharmony_ci	dev->name = strdup(name);
1804141cc406Sopenharmony_ci	dev->sane.name = dev->name;
1805141cc406Sopenharmony_ci
1806141cc406Sopenharmony_ci	*status = mc_discover_capabilities(s);
1807141cc406Sopenharmony_ci	if (*status != SANE_STATUS_GOOD)
1808141cc406Sopenharmony_ci		goto close;
1809141cc406Sopenharmony_ci
1810141cc406Sopenharmony_ci	if (source_list[0] == NULL || dev->cap->dpi_range.min == 0) {
1811141cc406Sopenharmony_ci		DBG(1, "something is wrong in the discovery process, aborting.\n");
1812141cc406Sopenharmony_ci		*status = SANE_STATUS_IO_ERROR;
1813141cc406Sopenharmony_ci		goto close;
1814141cc406Sopenharmony_ci	}
1815141cc406Sopenharmony_ci
1816141cc406Sopenharmony_ci	mc_dev_post_init(dev);
1817141cc406Sopenharmony_ci
1818141cc406Sopenharmony_ci	/* add this scanner to the device list */
1819141cc406Sopenharmony_ci	num_devices++;
1820141cc406Sopenharmony_ci	dev->missing = 0;
1821141cc406Sopenharmony_ci	dev->next = first_dev;
1822141cc406Sopenharmony_ci	first_dev = dev;
1823141cc406Sopenharmony_ci
1824141cc406Sopenharmony_ci	return s;
1825141cc406Sopenharmony_ci
1826141cc406Sopenharmony_ci	close:
1827141cc406Sopenharmony_ci	close_scanner(s);
1828141cc406Sopenharmony_ci	free(s);
1829141cc406Sopenharmony_ci	return NULL;
1830141cc406Sopenharmony_ci}
1831141cc406Sopenharmony_ci
1832141cc406Sopenharmony_ci#if HAVE_LIBSNMP
1833141cc406Sopenharmony_ci
1834141cc406Sopenharmony_ci/* Keep a linked list of already observed IP addresses */
1835141cc406Sopenharmony_ci/* typedef struct snmp_ip SNMP_IP; */
1836141cc406Sopenharmony_citypedef struct snmp_ip {
1837141cc406Sopenharmony_ci  char ip_addr[1024];
1838141cc406Sopenharmony_ci  struct snmp_ip*next;
1839141cc406Sopenharmony_ci} snmp_ip;
1840141cc406Sopenharmony_ci
1841141cc406Sopenharmony_citypedef struct {
1842141cc406Sopenharmony_ci  int nr;
1843141cc406Sopenharmony_ci  snmp_ip*handled;
1844141cc406Sopenharmony_ci  snmp_ip*detected;
1845141cc406Sopenharmony_ci} snmp_discovery_data;
1846141cc406Sopenharmony_ci
1847141cc406Sopenharmony_ci
1848141cc406Sopenharmony_ci/** Handle one SNMP response (whether received sync or async) and if describes
1849141cc406Sopenharmony_ci * a magicolor device, attach it. Returns the number of attached devices (0
1850141cc406Sopenharmony_ci * or 1) */
1851141cc406Sopenharmony_cistatic int
1852141cc406Sopenharmony_cimc_network_discovery_handle (struct snmp_pdu *pdu, snmp_discovery_data *magic)
1853141cc406Sopenharmony_ci{
1854141cc406Sopenharmony_ci	netsnmp_variable_list *varlist = pdu->variables, *vp;
1855141cc406Sopenharmony_ci	oid anOID[MAX_OID_LEN];
1856141cc406Sopenharmony_ci	size_t anOID_len = MAX_OID_LEN;
1857141cc406Sopenharmony_ci	/* Device information variables */
1858141cc406Sopenharmony_ci	char ip_addr[1024] = "";
1859141cc406Sopenharmony_ci	char model[1024] = "";
1860141cc406Sopenharmony_ci	char device[1024] = "";
1861141cc406Sopenharmony_ci	/* remote IP detection variables */
1862141cc406Sopenharmony_ci	netsnmp_indexed_addr_pair *responder = (netsnmp_indexed_addr_pair *) pdu->transport_data;
1863141cc406Sopenharmony_ci	struct sockaddr_in *remote = NULL;
1864141cc406Sopenharmony_ci	struct MagicolorCap *cap;
1865141cc406Sopenharmony_ci	snmp_ip *ip = NULL;
1866141cc406Sopenharmony_ci
1867141cc406Sopenharmony_ci	DBG(5, "%s: Handling SNMP response \n", __func__);
1868141cc406Sopenharmony_ci
1869141cc406Sopenharmony_ci	if (responder == NULL || pdu->transport_data_length != sizeof(netsnmp_indexed_addr_pair )) {
1870141cc406Sopenharmony_ci		DBG(1, "%s: Unable to extract IP address from SNMP response.\n",
1871141cc406Sopenharmony_ci		    __func__);
1872141cc406Sopenharmony_ci		return 0;
1873141cc406Sopenharmony_ci	}
1874141cc406Sopenharmony_ci	remote = (struct sockaddr_in *) &(responder->remote_addr);
1875141cc406Sopenharmony_ci	if (remote == NULL) {
1876141cc406Sopenharmony_ci		DBG(1, "%s: Unable to extract IP address from SNMP response.\n",
1877141cc406Sopenharmony_ci			__func__);
1878141cc406Sopenharmony_ci		return 0;
1879141cc406Sopenharmony_ci	}
1880141cc406Sopenharmony_ci	snprintf(ip_addr, sizeof(ip_addr), "%s", inet_ntoa(remote->sin_addr));
1881141cc406Sopenharmony_ci	DBG(35, "%s: IP Address of responder is %s\n", __func__, ip_addr);
1882141cc406Sopenharmony_ci	if (magic)
1883141cc406Sopenharmony_ci		ip = magic->handled;
1884141cc406Sopenharmony_ci	while (ip) {
1885141cc406Sopenharmony_ci		if (strcmp (ip->ip_addr, ip_addr) == 0) {
1886141cc406Sopenharmony_ci			DBG (5, "%s: Already handled device %s, skipping\n", __func__, ip_addr);
1887141cc406Sopenharmony_ci			return 0;
1888141cc406Sopenharmony_ci		}
1889141cc406Sopenharmony_ci		ip = ip->next;
1890141cc406Sopenharmony_ci	}
1891141cc406Sopenharmony_ci	if (magic) {
1892141cc406Sopenharmony_ci		snmp_ip *new_handled = malloc(sizeof(snmp_ip));
1893141cc406Sopenharmony_ci		strcpy (&new_handled->ip_addr[0], ip_addr);
1894141cc406Sopenharmony_ci		new_handled->next = magic->handled;
1895141cc406Sopenharmony_ci		magic->handled = new_handled;
1896141cc406Sopenharmony_ci	}
1897141cc406Sopenharmony_ci
1898141cc406Sopenharmony_ci	/* System Object ID (Unique OID identifying model)
1899141cc406Sopenharmony_ci	 * This determines whether we really have a magicolor device */
1900141cc406Sopenharmony_ci	anOID_len = MAX_OID_LEN;
1901141cc406Sopenharmony_ci	read_objid(MAGICOLOR_SNMP_SYSOBJECT_OID, anOID, &anOID_len);
1902141cc406Sopenharmony_ci	vp = find_varbind_in_list (varlist, anOID, anOID_len);
1903141cc406Sopenharmony_ci	if (vp) {
1904141cc406Sopenharmony_ci		size_t value_len = vp->val_len/sizeof(oid);
1905141cc406Sopenharmony_ci		if (vp->type != ASN_OBJECT_ID) {
1906141cc406Sopenharmony_ci			DBG (3, "%s: SystemObjectID does not return an OID, device is not a magicolor device\n", __func__);
1907141cc406Sopenharmony_ci			return 0;
1908141cc406Sopenharmony_ci		}
1909141cc406Sopenharmony_ci
1910141cc406Sopenharmony_ci		// Make sure that snprint_objid gives us just numbers, instead of a
1911141cc406Sopenharmony_ci		// SNMPv2-SMI::enterprises prefix.
1912141cc406Sopenharmony_ci		netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
1913141cc406Sopenharmony_ci				   NETSNMP_DS_LIB_OID_OUTPUT_FORMAT,
1914141cc406Sopenharmony_ci				   NETSNMP_OID_OUTPUT_NUMERIC);
1915141cc406Sopenharmony_ci
1916141cc406Sopenharmony_ci		snprint_objid (device, sizeof(device), vp->val.objid, value_len);
1917141cc406Sopenharmony_ci		DBG (5, "%s: Device object ID is '%s'\n", __func__, device);
1918141cc406Sopenharmony_ci
1919141cc406Sopenharmony_ci		anOID_len = MAX_OID_LEN;
1920141cc406Sopenharmony_ci		read_objid (MAGICOLOR_SNMP_DEVICE_TREE, anOID, &anOID_len);
1921141cc406Sopenharmony_ci		if (netsnmp_oid_is_subtree (anOID, anOID_len,
1922141cc406Sopenharmony_ci				vp->val.objid, value_len) == 0) {
1923141cc406Sopenharmony_ci			DBG (5, "%s: Device appears to be a magicolor device (OID=%s)\n", __func__, device);
1924141cc406Sopenharmony_ci		} else {
1925141cc406Sopenharmony_ci			DBG (5, "%s: Device is not a Magicolor device\n", __func__);
1926141cc406Sopenharmony_ci			return 0;
1927141cc406Sopenharmony_ci		}
1928141cc406Sopenharmony_ci	}
1929141cc406Sopenharmony_ci
1930141cc406Sopenharmony_ci	/* Retrieve sysDescr (i.e. model name) */
1931141cc406Sopenharmony_ci	anOID_len = MAX_OID_LEN;
1932141cc406Sopenharmony_ci	read_objid(MAGICOLOR_SNMP_SYSDESCR_OID, anOID, &anOID_len);
1933141cc406Sopenharmony_ci	vp = find_varbind_in_list (varlist, anOID, anOID_len);
1934141cc406Sopenharmony_ci	if (vp) {
1935141cc406Sopenharmony_ci		size_t model_len = min(vp->val_len, sizeof(model) - 1);
1936141cc406Sopenharmony_ci		memcpy(model, vp->val.string, model_len);
1937141cc406Sopenharmony_ci		model[model_len] = '\0';
1938141cc406Sopenharmony_ci		DBG (5, "%s: Found model: %s\n", __func__, model);
1939141cc406Sopenharmony_ci	}
1940141cc406Sopenharmony_ci
1941141cc406Sopenharmony_ci	DBG (1, "%s: Detected device '%s' on IP %s\n",
1942141cc406Sopenharmony_ci	     __func__, model, ip_addr);
1943141cc406Sopenharmony_ci
1944141cc406Sopenharmony_ci	vp = pdu->variables;
1945141cc406Sopenharmony_ci	/* TODO: attach the IP with attach_one_net(ip) */
1946141cc406Sopenharmony_ci	cap = mc_get_device_from_identification (device);
1947141cc406Sopenharmony_ci	if (cap) {
1948141cc406Sopenharmony_ci		DBG(1, "%s: Found autodiscovered device: %s (type 0x%x)\n", __func__, cap->model, cap->id);
1949141cc406Sopenharmony_ci		attach_one_net (ip_addr, cap->id);
1950141cc406Sopenharmony_ci		if (magic) {
1951141cc406Sopenharmony_ci			snmp_ip *new_detected = malloc(sizeof(snmp_ip));
1952141cc406Sopenharmony_ci			strcpy (&new_detected->ip_addr[0], ip_addr);
1953141cc406Sopenharmony_ci			new_detected->next = magic->detected;
1954141cc406Sopenharmony_ci			magic->detected = new_detected;
1955141cc406Sopenharmony_ci		}
1956141cc406Sopenharmony_ci		return 1;
1957141cc406Sopenharmony_ci	}
1958141cc406Sopenharmony_ci	return 0;
1959141cc406Sopenharmony_ci}
1960141cc406Sopenharmony_ci
1961141cc406Sopenharmony_cistatic int
1962141cc406Sopenharmony_cimc_network_discovery_cb (int operation, struct snmp_session *sp, int reqid,
1963141cc406Sopenharmony_ci			 struct snmp_pdu *pdu, void *magic)
1964141cc406Sopenharmony_ci{
1965141cc406Sopenharmony_ci	NOT_USED (reqid);
1966141cc406Sopenharmony_ci	NOT_USED (sp);
1967141cc406Sopenharmony_ci	DBG(5, "%s: Received broadcast response \n", __func__);
1968141cc406Sopenharmony_ci
1969141cc406Sopenharmony_ci	if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
1970141cc406Sopenharmony_ci		snmp_discovery_data *m = (snmp_discovery_data*)magic;
1971141cc406Sopenharmony_ci		int nr = mc_network_discovery_handle (pdu, m);
1972141cc406Sopenharmony_ci		m->nr += nr;
1973141cc406Sopenharmony_ci		DBG(5, "%s: Added %d discovered host(s) for SNMP response.\n", __func__, nr);
1974141cc406Sopenharmony_ci	}
1975141cc406Sopenharmony_ci
1976141cc406Sopenharmony_ci	return 0;
1977141cc406Sopenharmony_ci}
1978141cc406Sopenharmony_ci#endif
1979141cc406Sopenharmony_ci
1980141cc406Sopenharmony_ci/* Use SNMP for automatic network discovery. If host is given, try to detect
1981141cc406Sopenharmony_ci * that one host (using sync SNMP, otherwise send an SNMP broadcast (async).
1982141cc406Sopenharmony_ci */
1983141cc406Sopenharmony_cistatic int
1984141cc406Sopenharmony_cimc_network_discovery(const char*host)
1985141cc406Sopenharmony_ci{
1986141cc406Sopenharmony_ci#if HAVE_LIBSNMP
1987141cc406Sopenharmony_ci	netsnmp_session session, *ss;
1988141cc406Sopenharmony_ci	netsnmp_pdu *pdu;
1989141cc406Sopenharmony_ci	oid anOID[MAX_OID_LEN];
1990141cc406Sopenharmony_ci	size_t anOID_len = MAX_OID_LEN;
1991141cc406Sopenharmony_ci	snmp_discovery_data magic;
1992141cc406Sopenharmony_ci	magic.nr = 0;
1993141cc406Sopenharmony_ci	magic.handled = 0;
1994141cc406Sopenharmony_ci	magic.detected = 0;
1995141cc406Sopenharmony_ci
1996141cc406Sopenharmony_ci	DBG(1, "%s: running network discovery \n", __func__);
1997141cc406Sopenharmony_ci
1998141cc406Sopenharmony_ci	/* Win32: init winsock */
1999141cc406Sopenharmony_ci	SOCK_STARTUP;
2000141cc406Sopenharmony_ci	init_snmp("sane-magicolor-backend");
2001141cc406Sopenharmony_ci	snmp_sess_init (&session);
2002141cc406Sopenharmony_ci	session.version = SNMP_VERSION_2c;
2003141cc406Sopenharmony_ci	session.community = (u_char *) "public";
2004141cc406Sopenharmony_ci	session.community_len = strlen ((char *)session.community);
2005141cc406Sopenharmony_ci	if (host) {
2006141cc406Sopenharmony_ci		session.peername = (char *) host;
2007141cc406Sopenharmony_ci	} else {
2008141cc406Sopenharmony_ci		/* Do a network discovery via a broadcast */
2009141cc406Sopenharmony_ci		session.peername = "255.255.255.255";
2010141cc406Sopenharmony_ci		session.flags   |= SNMP_FLAGS_UDP_BROADCAST;
2011141cc406Sopenharmony_ci		session.callback = mc_network_discovery_cb;
2012141cc406Sopenharmony_ci		session.callback_magic = &magic;
2013141cc406Sopenharmony_ci	}
2014141cc406Sopenharmony_ci
2015141cc406Sopenharmony_ci	ss = snmp_open (&session); /* establish the session */
2016141cc406Sopenharmony_ci	if (!ss) {
2017141cc406Sopenharmony_ci		snmp_sess_perror ("ack", &session);
2018141cc406Sopenharmony_ci		SOCK_CLEANUP;
2019141cc406Sopenharmony_ci		return 0;
2020141cc406Sopenharmony_ci	}
2021141cc406Sopenharmony_ci
2022141cc406Sopenharmony_ci	/* Create the PDU for the data for our request and add the three
2023141cc406Sopenharmony_ci	 * desired OIDs to the PDU */
2024141cc406Sopenharmony_ci	pdu = snmp_pdu_create (SNMP_MSG_GET);
2025141cc406Sopenharmony_ci
2026141cc406Sopenharmony_ci	/* SNMPv2-MIB::sysDescr.0 */
2027141cc406Sopenharmony_ci	anOID_len = MAX_OID_LEN;
2028141cc406Sopenharmony_ci	if (read_objid(MAGICOLOR_SNMP_SYSDESCR_OID, anOID, &anOID_len)) {
2029141cc406Sopenharmony_ci		snmp_add_null_var (pdu, anOID, anOID_len);
2030141cc406Sopenharmony_ci	}
2031141cc406Sopenharmony_ci	/* SNMPv2-MIB::sysObjectID.0 */
2032141cc406Sopenharmony_ci	anOID_len = MAX_OID_LEN;
2033141cc406Sopenharmony_ci	if (read_objid(MAGICOLOR_SNMP_SYSOBJECT_OID, anOID, &anOID_len)) {
2034141cc406Sopenharmony_ci		snmp_add_null_var (pdu, anOID, anOID_len);
2035141cc406Sopenharmony_ci	}
2036141cc406Sopenharmony_ci	/* IF-MIB::ifPhysAddress.1 */
2037141cc406Sopenharmony_ci	anOID_len = MAX_OID_LEN;
2038141cc406Sopenharmony_ci	if (read_objid(MAGICOLOR_SNMP_MAC_OID, anOID, &anOID_len)) {
2039141cc406Sopenharmony_ci		snmp_add_null_var (pdu, anOID, anOID_len);
2040141cc406Sopenharmony_ci	}
2041141cc406Sopenharmony_ci	/* TODO: Add more interesting OIDs, in particular vendor OIDs */
2042141cc406Sopenharmony_ci
2043141cc406Sopenharmony_ci	/* Now send out the request and wait for responses for some time.
2044141cc406Sopenharmony_ci	 * If we get a response, connect to that device (in the callback),
2045141cc406Sopenharmony_ci	 * otherwise we probably don't have a magicolor device in the
2046141cc406Sopenharmony_ci	 * LAN (or SNMP is turned off, in which case we have no way to detect
2047141cc406Sopenharmony_ci	 * it.
2048141cc406Sopenharmony_ci	 */
2049141cc406Sopenharmony_ci	DBG(100, "%s: Sending SNMP packet\n", __func__);
2050141cc406Sopenharmony_ci	if (host) {
2051141cc406Sopenharmony_ci		/* sync request to given hostname, immediately read the reply */
2052141cc406Sopenharmony_ci		netsnmp_pdu *response = 0;
2053141cc406Sopenharmony_ci		int status = snmp_synch_response(ss, pdu, &response);
2054141cc406Sopenharmony_ci 		if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
2055141cc406Sopenharmony_ci			magic.nr = mc_network_discovery_handle (response, &magic);
2056141cc406Sopenharmony_ci		}
2057141cc406Sopenharmony_ci		if (response)
2058141cc406Sopenharmony_ci			snmp_free_pdu(response);
2059141cc406Sopenharmony_ci
2060141cc406Sopenharmony_ci
2061141cc406Sopenharmony_ci	} else {
2062141cc406Sopenharmony_ci		/* No hostname, so do a broadcast */
2063141cc406Sopenharmony_ci		struct timeval nowtime, endtime; /* end time for SNMP scan */
2064141cc406Sopenharmony_ci		struct timeval timeout;
2065141cc406Sopenharmony_ci		int i=0;
2066141cc406Sopenharmony_ci
2067141cc406Sopenharmony_ci		if (!snmp_send(ss, pdu)) {
2068141cc406Sopenharmony_ci			snmp_free_pdu(pdu);
2069141cc406Sopenharmony_ci			DBG(100, "%s: Sending SNMP packet NOT successful\n", __func__);
2070141cc406Sopenharmony_ci			return 0;
2071141cc406Sopenharmony_ci		}
2072141cc406Sopenharmony_ci		/* listen for responses for MC_AutoDetectionTimeout milliseconds: */
2073141cc406Sopenharmony_ci		/* First get the final timeout time */
2074141cc406Sopenharmony_ci		gettimeofday (&nowtime, NULL);
2075141cc406Sopenharmony_ci		timeout.tv_sec = MC_SNMP_Timeout / 1000;
2076141cc406Sopenharmony_ci		timeout.tv_usec = (MC_SNMP_Timeout % 1000) * 1000;
2077141cc406Sopenharmony_ci		timeradd (&nowtime, &timeout, &endtime);
2078141cc406Sopenharmony_ci
2079141cc406Sopenharmony_ci		while (timercmp(&nowtime, &endtime, <)) {
2080141cc406Sopenharmony_ci			int fds = 0, block = 0;
2081141cc406Sopenharmony_ci			fd_set fdset;
2082141cc406Sopenharmony_ci			DBG(1, "    loop=%d\n", i++);
2083141cc406Sopenharmony_ci			timeout.tv_sec = 0;
2084141cc406Sopenharmony_ci			/* Use a 125ms timeout for select. If we get a response,
2085141cc406Sopenharmony_ci			 * the loop will be entered earlier again, anyway */
2086141cc406Sopenharmony_ci			timeout.tv_usec = 125000;
2087141cc406Sopenharmony_ci			FD_ZERO (&fdset);
2088141cc406Sopenharmony_ci			snmp_select_info (&fds, &fdset, &timeout, &block);
2089141cc406Sopenharmony_ci			fds = select (fds, &fdset, NULL, NULL, /*block?NULL:*/&timeout);
2090141cc406Sopenharmony_ci			if (fds) snmp_read(&fdset);
2091141cc406Sopenharmony_ci			else snmp_timeout();
2092141cc406Sopenharmony_ci			gettimeofday(&nowtime, NULL);
2093141cc406Sopenharmony_ci		}
2094141cc406Sopenharmony_ci		/* Clean up the data in magic */
2095141cc406Sopenharmony_ci		while (magic.handled) {
2096141cc406Sopenharmony_ci		  snmp_ip *tmp = magic.handled->next;
2097141cc406Sopenharmony_ci		  free (magic.handled);
2098141cc406Sopenharmony_ci		  magic.handled = tmp;
2099141cc406Sopenharmony_ci		}
2100141cc406Sopenharmony_ci		while (magic.detected) {
2101141cc406Sopenharmony_ci		  snmp_ip *tmp = magic.detected->next;
2102141cc406Sopenharmony_ci		  free (magic.detected);
2103141cc406Sopenharmony_ci		  magic.detected = tmp;
2104141cc406Sopenharmony_ci		}
2105141cc406Sopenharmony_ci	}
2106141cc406Sopenharmony_ci
2107141cc406Sopenharmony_ci	/* Clean up */
2108141cc406Sopenharmony_ci	snmp_close(ss);
2109141cc406Sopenharmony_ci	SOCK_CLEANUP;
2110141cc406Sopenharmony_ci	DBG (5, "%s: Discovered %d host(s)\n", __func__, magic.nr);
2111141cc406Sopenharmony_ci	return magic.nr;
2112141cc406Sopenharmony_ci
2113141cc406Sopenharmony_ci#else
2114141cc406Sopenharmony_ci	DBG (1, "%s: net-snmp library not enabled, auto-detecting network scanners not supported.\n", __func__);
2115141cc406Sopenharmony_ci	NOT_USED (host);
2116141cc406Sopenharmony_ci	return 0;
2117141cc406Sopenharmony_ci#endif
2118141cc406Sopenharmony_ci}
2119141cc406Sopenharmony_ci
2120141cc406Sopenharmony_cistatic SANE_Status
2121141cc406Sopenharmony_ciattach(const char *name, int type)
2122141cc406Sopenharmony_ci{
2123141cc406Sopenharmony_ci	SANE_Status status;
2124141cc406Sopenharmony_ci	Magicolor_Scanner *s;
2125141cc406Sopenharmony_ci
2126141cc406Sopenharmony_ci	DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type);
2127141cc406Sopenharmony_ci
2128141cc406Sopenharmony_ci	s = device_detect(name, type, &status);
2129141cc406Sopenharmony_ci	if(s == NULL)
2130141cc406Sopenharmony_ci		return status;
2131141cc406Sopenharmony_ci
2132141cc406Sopenharmony_ci	close_scanner(s);
2133141cc406Sopenharmony_ci	free(s);
2134141cc406Sopenharmony_ci	return status;
2135141cc406Sopenharmony_ci}
2136141cc406Sopenharmony_ci
2137141cc406Sopenharmony_ciSANE_Status
2138141cc406Sopenharmony_ciattach_one_usb(const char *dev)
2139141cc406Sopenharmony_ci{
2140141cc406Sopenharmony_ci	DBG(7, "%s: dev = %s\n", __func__, dev);
2141141cc406Sopenharmony_ci	return attach(dev, SANE_MAGICOLOR_USB);
2142141cc406Sopenharmony_ci}
2143141cc406Sopenharmony_ci
2144141cc406Sopenharmony_cistatic SANE_Status
2145141cc406Sopenharmony_ciattach_one_net(const char *dev, unsigned int model)
2146141cc406Sopenharmony_ci{
2147141cc406Sopenharmony_ci	char name[1024];
2148141cc406Sopenharmony_ci
2149141cc406Sopenharmony_ci	DBG(7, "%s: dev = %s\n", __func__, dev);
2150141cc406Sopenharmony_ci	if (model > 0) {
2151141cc406Sopenharmony_ci		snprintf(name, 1024, "net:%s?model=0x%x", dev, model);
2152141cc406Sopenharmony_ci	} else {
2153141cc406Sopenharmony_ci		snprintf(name, 1024, "net:%s", dev);
2154141cc406Sopenharmony_ci	}
2155141cc406Sopenharmony_ci
2156141cc406Sopenharmony_ci	return attach(name, SANE_MAGICOLOR_NET);
2157141cc406Sopenharmony_ci}
2158141cc406Sopenharmony_ci
2159141cc406Sopenharmony_cistatic SANE_Status
2160141cc406Sopenharmony_ciattach_one_config(SANEI_Config __sane_unused__ *config, const char *line,
2161141cc406Sopenharmony_ci		  void *data)
2162141cc406Sopenharmony_ci{
2163141cc406Sopenharmony_ci	int vendor, product, timeout;
2164141cc406Sopenharmony_ci	SANE_Bool local_only = *(SANE_Bool*) data;
2165141cc406Sopenharmony_ci	int len = strlen(line);
2166141cc406Sopenharmony_ci
2167141cc406Sopenharmony_ci	DBG(7, "%s: len = %d, line = %s\n", __func__, len, line);
2168141cc406Sopenharmony_ci
2169141cc406Sopenharmony_ci	if (sscanf(line, "usb %i %i", &vendor, &product) == 2) {
2170141cc406Sopenharmony_ci		/* add the vendor and product IDs to the list of
2171141cc406Sopenharmony_ci		 *		   known devices before we call the attach function */
2172141cc406Sopenharmony_ci
2173141cc406Sopenharmony_ci		int numIds = sanei_magicolor_getNumberOfUSBProductIds();
2174141cc406Sopenharmony_ci
2175141cc406Sopenharmony_ci		if (vendor != SANE_MAGICOLOR_VENDOR_ID)
2176141cc406Sopenharmony_ci			return SANE_STATUS_INVAL; /* this is not a KONICA MINOLTA device */
2177141cc406Sopenharmony_ci
2178141cc406Sopenharmony_ci		sanei_magicolor_usb_product_ids[numIds - 1] = product;
2179141cc406Sopenharmony_ci		sanei_usb_attach_matching_devices(line, attach_one_usb);
2180141cc406Sopenharmony_ci
2181141cc406Sopenharmony_ci	} else if (strncmp(line, "usb", 3) == 0 && len == 3) {
2182141cc406Sopenharmony_ci		int i, numIds;
2183141cc406Sopenharmony_ci
2184141cc406Sopenharmony_ci		numIds = sanei_magicolor_getNumberOfUSBProductIds();
2185141cc406Sopenharmony_ci
2186141cc406Sopenharmony_ci		for (i = 0; i < numIds; i++) {
2187141cc406Sopenharmony_ci			sanei_usb_find_devices(SANE_MAGICOLOR_VENDOR_ID,
2188141cc406Sopenharmony_ci					       sanei_magicolor_usb_product_ids[i], attach_one_usb);
2189141cc406Sopenharmony_ci		}
2190141cc406Sopenharmony_ci
2191141cc406Sopenharmony_ci	} else if (strncmp(line, "net", 3) == 0) {
2192141cc406Sopenharmony_ci
2193141cc406Sopenharmony_ci		if (!local_only) {
2194141cc406Sopenharmony_ci			/* remove the "net" sub string */
2195141cc406Sopenharmony_ci			const char *name =
2196141cc406Sopenharmony_ci				sanei_config_skip_whitespace(line + 3);
2197141cc406Sopenharmony_ci			char IP[1024];
2198141cc406Sopenharmony_ci			unsigned int model = 0;
2199141cc406Sopenharmony_ci
2200141cc406Sopenharmony_ci			if (strncmp(name, "autodiscovery", 13) == 0) {
2201141cc406Sopenharmony_ci				DBG (50, "%s: Initiating network autodiscovervy via SNMP\n", __func__);
2202141cc406Sopenharmony_ci				mc_network_discovery(NULL);
2203141cc406Sopenharmony_ci			} else if (sscanf(name, "%s %x", IP, &model) == 2) {
2204141cc406Sopenharmony_ci				DBG(50, "%s: Using network device on IP %s, forcing model 0x%x\n", __func__, IP, model);
2205141cc406Sopenharmony_ci				attach_one_net(IP, model);
2206141cc406Sopenharmony_ci			} else {
2207141cc406Sopenharmony_ci				/* use SNMP to detect the type. If not successful,
2208141cc406Sopenharmony_ci				 * add the host with model type 0 */
2209141cc406Sopenharmony_ci				DBG(50, "%s: Using network device on IP %s, trying to autodetect model\n", __func__, IP);
2210141cc406Sopenharmony_ci				if (mc_network_discovery(name)==0) {
2211141cc406Sopenharmony_ci					DBG(1, "%s: Autodetecting device model failed, using default model\n", __func__);
2212141cc406Sopenharmony_ci					attach_one_net(name, 0);
2213141cc406Sopenharmony_ci				}
2214141cc406Sopenharmony_ci			}
2215141cc406Sopenharmony_ci		}
2216141cc406Sopenharmony_ci
2217141cc406Sopenharmony_ci	} else if (sscanf(line, "snmp-timeout %i\n", &timeout)) {
2218141cc406Sopenharmony_ci		/* Timeout for SNMP network discovery */
2219141cc406Sopenharmony_ci		DBG(50, "%s: SNMP timeout set to %d\n", __func__, timeout);
2220141cc406Sopenharmony_ci		MC_SNMP_Timeout = timeout;
2221141cc406Sopenharmony_ci
2222141cc406Sopenharmony_ci	} else if (sscanf(line, "scan-data-timeout %i\n", &timeout)) {
2223141cc406Sopenharmony_ci		/* Timeout for scan data requests */
2224141cc406Sopenharmony_ci		DBG(50, "%s: Scan data timeout set to %d\n", __func__, timeout);
2225141cc406Sopenharmony_ci		MC_Scan_Data_Timeout = timeout;
2226141cc406Sopenharmony_ci
2227141cc406Sopenharmony_ci	} else if (sscanf(line, "request-timeout %i\n", &timeout)) {
2228141cc406Sopenharmony_ci		/* Timeout for all other read requests */
2229141cc406Sopenharmony_ci		DBG(50, "%s: Request timeout set to %d\n", __func__, timeout);
2230141cc406Sopenharmony_ci		MC_Request_Timeout = timeout;
2231141cc406Sopenharmony_ci
2232141cc406Sopenharmony_ci	} else {
2233141cc406Sopenharmony_ci		/* TODO: Warning about unparsable line! */
2234141cc406Sopenharmony_ci	}
2235141cc406Sopenharmony_ci
2236141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2237141cc406Sopenharmony_ci}
2238141cc406Sopenharmony_ci
2239141cc406Sopenharmony_cistatic void
2240141cc406Sopenharmony_cifree_devices(void)
2241141cc406Sopenharmony_ci{
2242141cc406Sopenharmony_ci	Magicolor_Device *dev, *next;
2243141cc406Sopenharmony_ci
2244141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
2245141cc406Sopenharmony_ci
2246141cc406Sopenharmony_ci	for (dev = first_dev; dev; dev = next) {
2247141cc406Sopenharmony_ci		next = dev->next;
2248141cc406Sopenharmony_ci		free(dev->name);
2249141cc406Sopenharmony_ci		free(dev->model);
2250141cc406Sopenharmony_ci		free(dev);
2251141cc406Sopenharmony_ci	}
2252141cc406Sopenharmony_ci
2253141cc406Sopenharmony_ci	if (devlist)
2254141cc406Sopenharmony_ci		free(devlist);
2255141cc406Sopenharmony_ci	devlist = NULL;
2256141cc406Sopenharmony_ci	first_dev = NULL;
2257141cc406Sopenharmony_ci}
2258141cc406Sopenharmony_ci
2259141cc406Sopenharmony_ciSANE_Status
2260141cc406Sopenharmony_cisane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
2261141cc406Sopenharmony_ci{
2262141cc406Sopenharmony_ci	DBG_INIT();
2263141cc406Sopenharmony_ci	DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__);
2264141cc406Sopenharmony_ci
2265141cc406Sopenharmony_ci	DBG(1, "magicolor backend, version %i.%i.%i\n",
2266141cc406Sopenharmony_ci	    MAGICOLOR_VERSION, MAGICOLOR_REVISION, MAGICOLOR_BUILD);
2267141cc406Sopenharmony_ci
2268141cc406Sopenharmony_ci	if (version_code != NULL)
2269141cc406Sopenharmony_ci		*version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR,
2270141cc406Sopenharmony_ci						  MAGICOLOR_BUILD);
2271141cc406Sopenharmony_ci
2272141cc406Sopenharmony_ci	sanei_usb_init();
2273141cc406Sopenharmony_ci
2274141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2275141cc406Sopenharmony_ci}
2276141cc406Sopenharmony_ci
2277141cc406Sopenharmony_ci/* Clean up the list of attached scanners. */
2278141cc406Sopenharmony_civoid
2279141cc406Sopenharmony_cisane_exit(void)
2280141cc406Sopenharmony_ci{
2281141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
2282141cc406Sopenharmony_ci	free_devices();
2283141cc406Sopenharmony_ci}
2284141cc406Sopenharmony_ci
2285141cc406Sopenharmony_ciSANE_Status
2286141cc406Sopenharmony_cisane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
2287141cc406Sopenharmony_ci{
2288141cc406Sopenharmony_ci	Magicolor_Device *dev, *s, *prev=0;
2289141cc406Sopenharmony_ci	int i;
2290141cc406Sopenharmony_ci
2291141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
2292141cc406Sopenharmony_ci
2293141cc406Sopenharmony_ci	sanei_usb_init();
2294141cc406Sopenharmony_ci
2295141cc406Sopenharmony_ci	/* mark all existing scanners as missing, attach_one will remove mark */
2296141cc406Sopenharmony_ci	for (s = first_dev; s; s = s->next) {
2297141cc406Sopenharmony_ci		s->missing = 1;
2298141cc406Sopenharmony_ci	}
2299141cc406Sopenharmony_ci
2300141cc406Sopenharmony_ci	/* Read the config, mark each device as found, possibly add new devs */
2301141cc406Sopenharmony_ci	sanei_configure_attach(MAGICOLOR_CONFIG_FILE, NULL,
2302141cc406Sopenharmony_ci			       attach_one_config, &local_only);
2303141cc406Sopenharmony_ci
2304141cc406Sopenharmony_ci	/*delete missing scanners from list*/
2305141cc406Sopenharmony_ci	for (s = first_dev; s;) {
2306141cc406Sopenharmony_ci		if (s->missing) {
2307141cc406Sopenharmony_ci			DBG (5, "%s: missing scanner %s\n", __func__, s->name);
2308141cc406Sopenharmony_ci
2309141cc406Sopenharmony_ci			/*splice s out of list by changing pointer in prev to next*/
2310141cc406Sopenharmony_ci			if (prev) {
2311141cc406Sopenharmony_ci				prev->next = s->next;
2312141cc406Sopenharmony_ci				free (s);
2313141cc406Sopenharmony_ci				s = prev->next;
2314141cc406Sopenharmony_ci				num_devices--;
2315141cc406Sopenharmony_ci			} else {
2316141cc406Sopenharmony_ci				/*remove s from head of list */
2317141cc406Sopenharmony_ci				first_dev = s->next;
2318141cc406Sopenharmony_ci				free(s);
2319141cc406Sopenharmony_ci				s = first_dev;
2320141cc406Sopenharmony_ci				prev=NULL;
2321141cc406Sopenharmony_ci				num_devices--;
2322141cc406Sopenharmony_ci			}
2323141cc406Sopenharmony_ci		} else {
2324141cc406Sopenharmony_ci			prev = s;
2325141cc406Sopenharmony_ci			s = prev->next;
2326141cc406Sopenharmony_ci		}
2327141cc406Sopenharmony_ci	}
2328141cc406Sopenharmony_ci
2329141cc406Sopenharmony_ci	DBG (15, "%s: found %d scanner(s)\n", __func__, num_devices);
2330141cc406Sopenharmony_ci	for (s = first_dev; s; s=s->next) {
2331141cc406Sopenharmony_ci		DBG (15, "%s: found scanner %s\n", __func__, s->name);
2332141cc406Sopenharmony_ci	}
2333141cc406Sopenharmony_ci
2334141cc406Sopenharmony_ci	if (devlist)
2335141cc406Sopenharmony_ci		free (devlist);
2336141cc406Sopenharmony_ci
2337141cc406Sopenharmony_ci	devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
2338141cc406Sopenharmony_ci	if (!devlist) {
2339141cc406Sopenharmony_ci		DBG(1, "out of memory (line %d)\n", __LINE__);
2340141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
2341141cc406Sopenharmony_ci	}
2342141cc406Sopenharmony_ci
2343141cc406Sopenharmony_ci	DBG(5, "%s - results:\n", __func__);
2344141cc406Sopenharmony_ci
2345141cc406Sopenharmony_ci	for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) {
2346141cc406Sopenharmony_ci		DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model);
2347141cc406Sopenharmony_ci		devlist[i] = &dev->sane;
2348141cc406Sopenharmony_ci	}
2349141cc406Sopenharmony_ci
2350141cc406Sopenharmony_ci	devlist[i] = NULL;
2351141cc406Sopenharmony_ci
2352141cc406Sopenharmony_ci	if(device_list){
2353141cc406Sopenharmony_ci		*device_list = devlist;
2354141cc406Sopenharmony_ci	}
2355141cc406Sopenharmony_ci
2356141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2357141cc406Sopenharmony_ci}
2358141cc406Sopenharmony_ci
2359141cc406Sopenharmony_cistatic SANE_Status
2360141cc406Sopenharmony_ciinit_options(Magicolor_Scanner *s)
2361141cc406Sopenharmony_ci{
2362141cc406Sopenharmony_ci	int i;
2363141cc406Sopenharmony_ci	SANE_Word *res_list;
2364141cc406Sopenharmony_ci
2365141cc406Sopenharmony_ci	for (i = 0; i < NUM_OPTIONS; i++) {
2366141cc406Sopenharmony_ci		s->opt[i].size = sizeof(SANE_Word);
2367141cc406Sopenharmony_ci		s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
2368141cc406Sopenharmony_ci	}
2369141cc406Sopenharmony_ci
2370141cc406Sopenharmony_ci	s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
2371141cc406Sopenharmony_ci	s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
2372141cc406Sopenharmony_ci	s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
2373141cc406Sopenharmony_ci	s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
2374141cc406Sopenharmony_ci	s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
2375141cc406Sopenharmony_ci
2376141cc406Sopenharmony_ci	/* "Scan Mode" group: */
2377141cc406Sopenharmony_ci
2378141cc406Sopenharmony_ci	s->opt[OPT_MODE_GROUP].name = SANE_NAME_STANDARD;
2379141cc406Sopenharmony_ci	s->opt[OPT_MODE_GROUP].title = SANE_TITLE_STANDARD;
2380141cc406Sopenharmony_ci	s->opt[OPT_MODE_GROUP].desc = SANE_DESC_STANDARD;
2381141cc406Sopenharmony_ci	s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
2382141cc406Sopenharmony_ci	s->opt[OPT_MODE_GROUP].cap = 0;
2383141cc406Sopenharmony_ci
2384141cc406Sopenharmony_ci	/* scan mode */
2385141cc406Sopenharmony_ci	s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
2386141cc406Sopenharmony_ci	s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
2387141cc406Sopenharmony_ci	s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
2388141cc406Sopenharmony_ci	s->opt[OPT_MODE].type = SANE_TYPE_STRING;
2389141cc406Sopenharmony_ci	s->opt[OPT_MODE].size = max_string_size(mode_list);
2390141cc406Sopenharmony_ci	s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
2391141cc406Sopenharmony_ci	s->opt[OPT_MODE].constraint.string_list = mode_list;
2392141cc406Sopenharmony_ci	s->val[OPT_MODE].w = 0;	/* Binary */
2393141cc406Sopenharmony_ci
2394141cc406Sopenharmony_ci	/* bit depth */
2395141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
2396141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
2397141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
2398141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
2399141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
2400141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
2401141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->cap->depth_list;
2402141cc406Sopenharmony_ci	s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
2403141cc406Sopenharmony_ci	s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1];	/* the first "real" element is the default */
2404141cc406Sopenharmony_ci
2405141cc406Sopenharmony_ci	if (s->hw->cap->depth_list[0] == 1)	/* only one element in the list -> hide the option */
2406141cc406Sopenharmony_ci		s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
2407141cc406Sopenharmony_ci
2408141cc406Sopenharmony_ci
2409141cc406Sopenharmony_ci	/* brightness */
2410141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
2411141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
2412141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
2413141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
2414141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
2415141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
2416141cc406Sopenharmony_ci	s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cap->brightness;
2417141cc406Sopenharmony_ci	s->val[OPT_BRIGHTNESS].w = 5;	/* Normal */
2418141cc406Sopenharmony_ci
2419141cc406Sopenharmony_ci	/* resolution */
2420141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
2421141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
2422141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
2423141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
2424141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
2425141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
2426141cc406Sopenharmony_ci	res_list = malloc((s->hw->cap->res_list_size + 1) * sizeof(SANE_Word));
2427141cc406Sopenharmony_ci	if (res_list == NULL) {
2428141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
2429141cc406Sopenharmony_ci	}
2430141cc406Sopenharmony_ci	*(res_list) = s->hw->cap->res_list_size;
2431141cc406Sopenharmony_ci	memcpy(&(res_list[1]), s->hw->cap->res_list, s->hw->cap->res_list_size * sizeof(SANE_Word));
2432141cc406Sopenharmony_ci	s->opt[OPT_RESOLUTION].constraint.word_list = res_list;
2433141cc406Sopenharmony_ci	s->val[OPT_RESOLUTION].w = s->hw->cap->dpi_range.min;
2434141cc406Sopenharmony_ci
2435141cc406Sopenharmony_ci
2436141cc406Sopenharmony_ci	/* preview */
2437141cc406Sopenharmony_ci	s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
2438141cc406Sopenharmony_ci	s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
2439141cc406Sopenharmony_ci	s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
2440141cc406Sopenharmony_ci	s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
2441141cc406Sopenharmony_ci	s->val[OPT_PREVIEW].w = SANE_FALSE;
2442141cc406Sopenharmony_ci
2443141cc406Sopenharmony_ci	/* source */
2444141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
2445141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
2446141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
2447141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
2448141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].size = max_string_size(source_list);
2449141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
2450141cc406Sopenharmony_ci	s->opt[OPT_SOURCE].constraint.string_list = source_list;
2451141cc406Sopenharmony_ci	s->val[OPT_SOURCE].w = 0;	/* always use Flatbed as default */
2452141cc406Sopenharmony_ci
2453141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].name = "adf-mode";
2454141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode");
2455141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].desc =
2456141cc406Sopenharmony_ci	SANE_I18N("Selects the ADF mode (simplex/duplex)");
2457141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
2458141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list);
2459141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
2460141cc406Sopenharmony_ci	s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
2461141cc406Sopenharmony_ci	s->val[OPT_ADF_MODE].w = 0;	/* simplex */
2462141cc406Sopenharmony_ci	if ((!s->hw->cap->ADF) || (s->hw->cap->adf_duplex == SANE_FALSE))
2463141cc406Sopenharmony_ci		s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
2464141cc406Sopenharmony_ci
2465141cc406Sopenharmony_ci
2466141cc406Sopenharmony_ci	/* "Geometry" group: */
2467141cc406Sopenharmony_ci	s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
2468141cc406Sopenharmony_ci	s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY;
2469141cc406Sopenharmony_ci	s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY;
2470141cc406Sopenharmony_ci	s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
2471141cc406Sopenharmony_ci	s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
2472141cc406Sopenharmony_ci
2473141cc406Sopenharmony_ci	/* top-left x */
2474141cc406Sopenharmony_ci	s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
2475141cc406Sopenharmony_ci	s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
2476141cc406Sopenharmony_ci	s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
2477141cc406Sopenharmony_ci	s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
2478141cc406Sopenharmony_ci	s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
2479141cc406Sopenharmony_ci	s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
2480141cc406Sopenharmony_ci	s->opt[OPT_TL_X].constraint.range = s->hw->x_range;
2481141cc406Sopenharmony_ci	s->val[OPT_TL_X].w = 0;
2482141cc406Sopenharmony_ci
2483141cc406Sopenharmony_ci	/* top-left y */
2484141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
2485141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
2486141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
2487141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
2488141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
2489141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
2490141cc406Sopenharmony_ci	s->opt[OPT_TL_Y].constraint.range = s->hw->y_range;
2491141cc406Sopenharmony_ci	s->val[OPT_TL_Y].w = 0;
2492141cc406Sopenharmony_ci
2493141cc406Sopenharmony_ci	/* bottom-right x */
2494141cc406Sopenharmony_ci	s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
2495141cc406Sopenharmony_ci	s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
2496141cc406Sopenharmony_ci	s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
2497141cc406Sopenharmony_ci	s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
2498141cc406Sopenharmony_ci	s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
2499141cc406Sopenharmony_ci	s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
2500141cc406Sopenharmony_ci	s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
2501141cc406Sopenharmony_ci	s->val[OPT_BR_X].w = s->hw->x_range->max;
2502141cc406Sopenharmony_ci
2503141cc406Sopenharmony_ci	/* bottom-right y */
2504141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
2505141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
2506141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
2507141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
2508141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
2509141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
2510141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
2511141cc406Sopenharmony_ci	s->val[OPT_BR_Y].w = s->hw->y_range->max;
2512141cc406Sopenharmony_ci
2513141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2514141cc406Sopenharmony_ci}
2515141cc406Sopenharmony_ci
2516141cc406Sopenharmony_ciSANE_Status
2517141cc406Sopenharmony_cisane_open(SANE_String_Const name, SANE_Handle *handle)
2518141cc406Sopenharmony_ci{
2519141cc406Sopenharmony_ci	SANE_Status status;
2520141cc406Sopenharmony_ci	Magicolor_Scanner *s = NULL;
2521141cc406Sopenharmony_ci
2522141cc406Sopenharmony_ci	int l = strlen(name);
2523141cc406Sopenharmony_ci
2524141cc406Sopenharmony_ci	DBG(7, "%s: name = %s\n", __func__, name);
2525141cc406Sopenharmony_ci
2526141cc406Sopenharmony_ci	/* probe if empty device name provided */
2527141cc406Sopenharmony_ci	if (l == 0) {
2528141cc406Sopenharmony_ci
2529141cc406Sopenharmony_ci		status = sane_get_devices(NULL,0);
2530141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD) {
2531141cc406Sopenharmony_ci			return status;
2532141cc406Sopenharmony_ci		}
2533141cc406Sopenharmony_ci
2534141cc406Sopenharmony_ci		if (first_dev == NULL) {
2535141cc406Sopenharmony_ci			DBG(1, "no device detected\n");
2536141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
2537141cc406Sopenharmony_ci		}
2538141cc406Sopenharmony_ci
2539141cc406Sopenharmony_ci		s = device_detect(first_dev->sane.name, first_dev->connection,
2540141cc406Sopenharmony_ci					&status);
2541141cc406Sopenharmony_ci		if (s == NULL) {
2542141cc406Sopenharmony_ci			DBG(1, "cannot open a perfectly valid device (%s),"
2543141cc406Sopenharmony_ci				" please report to the authors\n", name);
2544141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
2545141cc406Sopenharmony_ci		}
2546141cc406Sopenharmony_ci
2547141cc406Sopenharmony_ci	} else {
2548141cc406Sopenharmony_ci
2549141cc406Sopenharmony_ci		if (strncmp(name, "net:", 4) == 0) {
2550141cc406Sopenharmony_ci			s = device_detect(name, SANE_MAGICOLOR_NET, &status);
2551141cc406Sopenharmony_ci			if (s == NULL)
2552141cc406Sopenharmony_ci				return status;
2553141cc406Sopenharmony_ci		} else if (strncmp(name, "libusb:", 7) == 0) {
2554141cc406Sopenharmony_ci			s = device_detect(name, SANE_MAGICOLOR_USB, &status);
2555141cc406Sopenharmony_ci			if (s == NULL)
2556141cc406Sopenharmony_ci				return status;
2557141cc406Sopenharmony_ci		} else {
2558141cc406Sopenharmony_ci
2559141cc406Sopenharmony_ci			/* as a last resort, check for a match
2560141cc406Sopenharmony_ci			 * in the device list. This should handle platforms without libusb.
2561141cc406Sopenharmony_ci			 */
2562141cc406Sopenharmony_ci			if (first_dev == NULL) {
2563141cc406Sopenharmony_ci				status = sane_get_devices(NULL,0);
2564141cc406Sopenharmony_ci				if (status != SANE_STATUS_GOOD) {
2565141cc406Sopenharmony_ci					return status;
2566141cc406Sopenharmony_ci				}
2567141cc406Sopenharmony_ci			}
2568141cc406Sopenharmony_ci
2569141cc406Sopenharmony_ci			s = device_detect(name, SANE_MAGICOLOR_NODEV, &status);
2570141cc406Sopenharmony_ci			if (s == NULL) {
2571141cc406Sopenharmony_ci				DBG(1, "invalid device name: %s\n", name);
2572141cc406Sopenharmony_ci				return SANE_STATUS_INVAL;
2573141cc406Sopenharmony_ci			}
2574141cc406Sopenharmony_ci		}
2575141cc406Sopenharmony_ci	}
2576141cc406Sopenharmony_ci
2577141cc406Sopenharmony_ci
2578141cc406Sopenharmony_ci	/* s is always valid here */
2579141cc406Sopenharmony_ci
2580141cc406Sopenharmony_ci	DBG(1, "handle obtained\n");
2581141cc406Sopenharmony_ci
2582141cc406Sopenharmony_ci	init_options(s);
2583141cc406Sopenharmony_ci
2584141cc406Sopenharmony_ci	*handle = (SANE_Handle) s;
2585141cc406Sopenharmony_ci
2586141cc406Sopenharmony_ci	status = open_scanner(s);
2587141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
2588141cc406Sopenharmony_ci		free(s);
2589141cc406Sopenharmony_ci		return status;
2590141cc406Sopenharmony_ci	}
2591141cc406Sopenharmony_ci
2592141cc406Sopenharmony_ci	return status;
2593141cc406Sopenharmony_ci}
2594141cc406Sopenharmony_ci
2595141cc406Sopenharmony_civoid
2596141cc406Sopenharmony_cisane_close(SANE_Handle handle)
2597141cc406Sopenharmony_ci{
2598141cc406Sopenharmony_ci	Magicolor_Scanner *s;
2599141cc406Sopenharmony_ci
2600141cc406Sopenharmony_ci	/*
2601141cc406Sopenharmony_ci	 * XXX Test if there is still data pending from
2602141cc406Sopenharmony_ci	 * the scanner. If so, then do a cancel
2603141cc406Sopenharmony_ci	 */
2604141cc406Sopenharmony_ci
2605141cc406Sopenharmony_ci	s = (Magicolor_Scanner *) handle;
2606141cc406Sopenharmony_ci
2607141cc406Sopenharmony_ci	if (s->fd != -1)
2608141cc406Sopenharmony_ci		close_scanner(s);
2609141cc406Sopenharmony_ci
2610141cc406Sopenharmony_ci	free(s);
2611141cc406Sopenharmony_ci}
2612141cc406Sopenharmony_ci
2613141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
2614141cc406Sopenharmony_cisane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
2615141cc406Sopenharmony_ci{
2616141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
2617141cc406Sopenharmony_ci
2618141cc406Sopenharmony_ci	if (option < 0 || option >= NUM_OPTIONS)
2619141cc406Sopenharmony_ci		return NULL;
2620141cc406Sopenharmony_ci
2621141cc406Sopenharmony_ci	return s->opt + option;
2622141cc406Sopenharmony_ci}
2623141cc406Sopenharmony_ci
2624141cc406Sopenharmony_cistatic const SANE_String_Const *
2625141cc406Sopenharmony_cisearch_string_list(const SANE_String_Const *list, SANE_String value)
2626141cc406Sopenharmony_ci{
2627141cc406Sopenharmony_ci	while (*list != NULL && strcmp(value, *list) != 0)
2628141cc406Sopenharmony_ci		list++;
2629141cc406Sopenharmony_ci
2630141cc406Sopenharmony_ci	return ((*list == NULL) ? NULL : list);
2631141cc406Sopenharmony_ci}
2632141cc406Sopenharmony_ci
2633141cc406Sopenharmony_ci/*
2634141cc406Sopenharmony_ci    Activate, deactivate an option. Subroutines so we can add
2635141cc406Sopenharmony_ci    debugging info if we want. The change flag is set to TRUE
2636141cc406Sopenharmony_ci    if we changed an option. If we did not change an option,
2637141cc406Sopenharmony_ci    then the value of the changed flag is not modified.
2638141cc406Sopenharmony_ci*/
2639141cc406Sopenharmony_ci
2640141cc406Sopenharmony_cistatic void
2641141cc406Sopenharmony_ciactivateOption(Magicolor_Scanner *s, SANE_Int option, SANE_Bool *change)
2642141cc406Sopenharmony_ci{
2643141cc406Sopenharmony_ci	if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
2644141cc406Sopenharmony_ci		s->opt[option].cap &= ~SANE_CAP_INACTIVE;
2645141cc406Sopenharmony_ci		*change = SANE_TRUE;
2646141cc406Sopenharmony_ci	}
2647141cc406Sopenharmony_ci}
2648141cc406Sopenharmony_ci
2649141cc406Sopenharmony_cistatic void
2650141cc406Sopenharmony_cideactivateOption(Magicolor_Scanner *s, SANE_Int option, SANE_Bool *change)
2651141cc406Sopenharmony_ci{
2652141cc406Sopenharmony_ci	if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
2653141cc406Sopenharmony_ci		s->opt[option].cap |= SANE_CAP_INACTIVE;
2654141cc406Sopenharmony_ci		*change = SANE_TRUE;
2655141cc406Sopenharmony_ci	}
2656141cc406Sopenharmony_ci}
2657141cc406Sopenharmony_ci
2658141cc406Sopenharmony_cistatic SANE_Status
2659141cc406Sopenharmony_cigetvalue(SANE_Handle handle, SANE_Int option, void *value)
2660141cc406Sopenharmony_ci{
2661141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
2662141cc406Sopenharmony_ci	SANE_Option_Descriptor *sopt = &(s->opt[option]);
2663141cc406Sopenharmony_ci	Option_Value *sval = &(s->val[option]);
2664141cc406Sopenharmony_ci
2665141cc406Sopenharmony_ci	DBG(17, "%s: option = %d\n", __func__, option);
2666141cc406Sopenharmony_ci
2667141cc406Sopenharmony_ci	switch (option) {
2668141cc406Sopenharmony_ci
2669141cc406Sopenharmony_ci	case OPT_NUM_OPTS:
2670141cc406Sopenharmony_ci	case OPT_BIT_DEPTH:
2671141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
2672141cc406Sopenharmony_ci	case OPT_RESOLUTION:
2673141cc406Sopenharmony_ci	case OPT_PREVIEW:
2674141cc406Sopenharmony_ci	case OPT_TL_X:
2675141cc406Sopenharmony_ci	case OPT_TL_Y:
2676141cc406Sopenharmony_ci	case OPT_BR_X:
2677141cc406Sopenharmony_ci	case OPT_BR_Y:
2678141cc406Sopenharmony_ci		*((SANE_Word *) value) = sval->w;
2679141cc406Sopenharmony_ci		break;
2680141cc406Sopenharmony_ci
2681141cc406Sopenharmony_ci	case OPT_MODE:
2682141cc406Sopenharmony_ci	case OPT_SOURCE:
2683141cc406Sopenharmony_ci	case OPT_ADF_MODE:
2684141cc406Sopenharmony_ci		strcpy((char *) value, sopt->constraint.string_list[sval->w]);
2685141cc406Sopenharmony_ci		break;
2686141cc406Sopenharmony_ci
2687141cc406Sopenharmony_ci	default:
2688141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
2689141cc406Sopenharmony_ci	}
2690141cc406Sopenharmony_ci
2691141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2692141cc406Sopenharmony_ci}
2693141cc406Sopenharmony_ci
2694141cc406Sopenharmony_ci
2695141cc406Sopenharmony_ci/*
2696141cc406Sopenharmony_ci * Handles setting the source (flatbed, or auto document feeder (ADF)).
2697141cc406Sopenharmony_ci *
2698141cc406Sopenharmony_ci */
2699141cc406Sopenharmony_ci
2700141cc406Sopenharmony_cistatic void
2701141cc406Sopenharmony_cichange_source(Magicolor_Scanner *s, SANE_Int optindex, char *value)
2702141cc406Sopenharmony_ci{
2703141cc406Sopenharmony_ci	int force_max = SANE_FALSE;
2704141cc406Sopenharmony_ci	SANE_Bool dummy;
2705141cc406Sopenharmony_ci
2706141cc406Sopenharmony_ci	DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex,
2707141cc406Sopenharmony_ci	    value);
2708141cc406Sopenharmony_ci
2709141cc406Sopenharmony_ci	if (s->val[OPT_SOURCE].w == optindex)
2710141cc406Sopenharmony_ci		return;
2711141cc406Sopenharmony_ci
2712141cc406Sopenharmony_ci	s->val[OPT_SOURCE].w = optindex;
2713141cc406Sopenharmony_ci
2714141cc406Sopenharmony_ci	if (s->val[OPT_TL_X].w == s->hw->x_range->min
2715141cc406Sopenharmony_ci	    && s->val[OPT_TL_Y].w == s->hw->y_range->min
2716141cc406Sopenharmony_ci	    && s->val[OPT_BR_X].w == s->hw->x_range->max
2717141cc406Sopenharmony_ci	    && s->val[OPT_BR_Y].w == s->hw->y_range->max) {
2718141cc406Sopenharmony_ci		force_max = SANE_TRUE;
2719141cc406Sopenharmony_ci	}
2720141cc406Sopenharmony_ci
2721141cc406Sopenharmony_ci	if (strcmp(ADF_STR, value) == 0) {
2722141cc406Sopenharmony_ci		s->hw->x_range = &s->hw->cap->adf_x_range;
2723141cc406Sopenharmony_ci		s->hw->y_range = &s->hw->cap->adf_y_range;
2724141cc406Sopenharmony_ci		if (s->hw->cap->adf_duplex) {
2725141cc406Sopenharmony_ci			activateOption(s, OPT_ADF_MODE, &dummy);
2726141cc406Sopenharmony_ci		} else {
2727141cc406Sopenharmony_ci			deactivateOption(s, OPT_ADF_MODE, &dummy);
2728141cc406Sopenharmony_ci			s->val[OPT_ADF_MODE].w = 0;
2729141cc406Sopenharmony_ci		}
2730141cc406Sopenharmony_ci
2731141cc406Sopenharmony_ci		DBG(1, "adf activated (%d)\n",s->hw->cap->adf_duplex);
2732141cc406Sopenharmony_ci
2733141cc406Sopenharmony_ci	} else {
2734141cc406Sopenharmony_ci		/* ADF not active */
2735141cc406Sopenharmony_ci		s->hw->x_range = &s->hw->cap->fbf_x_range;
2736141cc406Sopenharmony_ci		s->hw->y_range = &s->hw->cap->fbf_y_range;
2737141cc406Sopenharmony_ci
2738141cc406Sopenharmony_ci		deactivateOption(s, OPT_ADF_MODE, &dummy);
2739141cc406Sopenharmony_ci	}
2740141cc406Sopenharmony_ci
2741141cc406Sopenharmony_ci	s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
2742141cc406Sopenharmony_ci	s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
2743141cc406Sopenharmony_ci
2744141cc406Sopenharmony_ci	if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
2745141cc406Sopenharmony_ci		s->val[OPT_TL_X].w = s->hw->x_range->min;
2746141cc406Sopenharmony_ci
2747141cc406Sopenharmony_ci	if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
2748141cc406Sopenharmony_ci		s->val[OPT_TL_Y].w = s->hw->y_range->min;
2749141cc406Sopenharmony_ci
2750141cc406Sopenharmony_ci	if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
2751141cc406Sopenharmony_ci		s->val[OPT_BR_X].w = s->hw->x_range->max;
2752141cc406Sopenharmony_ci
2753141cc406Sopenharmony_ci	if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
2754141cc406Sopenharmony_ci		s->val[OPT_BR_Y].w = s->hw->y_range->max;
2755141cc406Sopenharmony_ci
2756141cc406Sopenharmony_ci}
2757141cc406Sopenharmony_ci
2758141cc406Sopenharmony_cistatic SANE_Status
2759141cc406Sopenharmony_cisetvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
2760141cc406Sopenharmony_ci{
2761141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
2762141cc406Sopenharmony_ci	SANE_Option_Descriptor *sopt = &(s->opt[option]);
2763141cc406Sopenharmony_ci	Option_Value *sval = &(s->val[option]);
2764141cc406Sopenharmony_ci
2765141cc406Sopenharmony_ci	SANE_Status status;
2766141cc406Sopenharmony_ci	const SANE_String_Const *optval = NULL;
2767141cc406Sopenharmony_ci	int optindex = 0;
2768141cc406Sopenharmony_ci	SANE_Bool reload = SANE_FALSE;
2769141cc406Sopenharmony_ci
2770141cc406Sopenharmony_ci	DBG(17, "%s: option = %d, value = %p, as word: %d\n", __func__, option, value, *(SANE_Word *) value);
2771141cc406Sopenharmony_ci
2772141cc406Sopenharmony_ci	status = sanei_constrain_value(sopt, value, info);
2773141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2774141cc406Sopenharmony_ci		return status;
2775141cc406Sopenharmony_ci
2776141cc406Sopenharmony_ci	if (info && value && (*info & SANE_INFO_INEXACT)
2777141cc406Sopenharmony_ci	    && sopt->type == SANE_TYPE_INT)
2778141cc406Sopenharmony_ci		DBG(17, "%s: constrained val = %d\n", __func__,
2779141cc406Sopenharmony_ci		    *(SANE_Word *) value);
2780141cc406Sopenharmony_ci
2781141cc406Sopenharmony_ci	if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) {
2782141cc406Sopenharmony_ci		optval = search_string_list(sopt->constraint.string_list,
2783141cc406Sopenharmony_ci					    (char *) value);
2784141cc406Sopenharmony_ci		if (optval == NULL)
2785141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
2786141cc406Sopenharmony_ci		optindex = optval - sopt->constraint.string_list;
2787141cc406Sopenharmony_ci	}
2788141cc406Sopenharmony_ci
2789141cc406Sopenharmony_ci	switch (option) {
2790141cc406Sopenharmony_ci
2791141cc406Sopenharmony_ci	case OPT_MODE:
2792141cc406Sopenharmony_ci	{
2793141cc406Sopenharmony_ci		sval->w = optindex;
2794141cc406Sopenharmony_ci		/* if binary, then disable the bit depth selection */
2795141cc406Sopenharmony_ci		if (optindex == 0) {
2796141cc406Sopenharmony_ci			s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
2797141cc406Sopenharmony_ci		} else {
2798141cc406Sopenharmony_ci			if (s->hw->cap->depth_list[0] == 1)
2799141cc406Sopenharmony_ci				s->opt[OPT_BIT_DEPTH].cap |=
2800141cc406Sopenharmony_ci					SANE_CAP_INACTIVE;
2801141cc406Sopenharmony_ci			else {
2802141cc406Sopenharmony_ci				s->opt[OPT_BIT_DEPTH].cap &=
2803141cc406Sopenharmony_ci					~SANE_CAP_INACTIVE;
2804141cc406Sopenharmony_ci				s->val[OPT_BIT_DEPTH].w =
2805141cc406Sopenharmony_ci					mode_params[optindex].depth;
2806141cc406Sopenharmony_ci			}
2807141cc406Sopenharmony_ci		}
2808141cc406Sopenharmony_ci		reload = SANE_TRUE;
2809141cc406Sopenharmony_ci		break;
2810141cc406Sopenharmony_ci	}
2811141cc406Sopenharmony_ci
2812141cc406Sopenharmony_ci	case OPT_BIT_DEPTH:
2813141cc406Sopenharmony_ci		sval->w = *((SANE_Word *) value);
2814141cc406Sopenharmony_ci		mode_params[s->val[OPT_MODE].w].depth = sval->w;
2815141cc406Sopenharmony_ci		reload = SANE_TRUE;
2816141cc406Sopenharmony_ci		break;
2817141cc406Sopenharmony_ci
2818141cc406Sopenharmony_ci	case OPT_RESOLUTION:
2819141cc406Sopenharmony_ci		sval->w = *((SANE_Word *) value);
2820141cc406Sopenharmony_ci		DBG(17, "setting resolution to %d\n", sval->w);
2821141cc406Sopenharmony_ci		reload = SANE_TRUE;
2822141cc406Sopenharmony_ci		break;
2823141cc406Sopenharmony_ci
2824141cc406Sopenharmony_ci	case OPT_BR_X:
2825141cc406Sopenharmony_ci	case OPT_BR_Y:
2826141cc406Sopenharmony_ci		if (SANE_UNFIX(*((SANE_Word *) value)) == 0) {
2827141cc406Sopenharmony_ci			DBG(17, "invalid br-x or br-y\n");
2828141cc406Sopenharmony_ci			return SANE_STATUS_INVAL;
2829141cc406Sopenharmony_ci		}
2830141cc406Sopenharmony_ci		// fall through
2831141cc406Sopenharmony_ci	case OPT_TL_X:
2832141cc406Sopenharmony_ci	case OPT_TL_Y:
2833141cc406Sopenharmony_ci		sval->w = *((SANE_Word *) value);
2834141cc406Sopenharmony_ci		DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w));
2835141cc406Sopenharmony_ci		if (NULL != info)
2836141cc406Sopenharmony_ci			*info |= SANE_INFO_RELOAD_PARAMS;
2837141cc406Sopenharmony_ci		break;
2838141cc406Sopenharmony_ci
2839141cc406Sopenharmony_ci	case OPT_SOURCE:
2840141cc406Sopenharmony_ci		change_source(s, optindex, (char *) value);
2841141cc406Sopenharmony_ci		reload = SANE_TRUE;
2842141cc406Sopenharmony_ci		break;
2843141cc406Sopenharmony_ci
2844141cc406Sopenharmony_ci	case OPT_ADF_MODE:
2845141cc406Sopenharmony_ci		sval->w = optindex;	/* Simple lists */
2846141cc406Sopenharmony_ci		break;
2847141cc406Sopenharmony_ci
2848141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
2849141cc406Sopenharmony_ci	case OPT_PREVIEW:	/* needed? */
2850141cc406Sopenharmony_ci		sval->w = *((SANE_Word *) value);
2851141cc406Sopenharmony_ci		break;
2852141cc406Sopenharmony_ci
2853141cc406Sopenharmony_ci	default:
2854141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
2855141cc406Sopenharmony_ci	}
2856141cc406Sopenharmony_ci
2857141cc406Sopenharmony_ci	if (reload && info != NULL)
2858141cc406Sopenharmony_ci		*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
2859141cc406Sopenharmony_ci
2860141cc406Sopenharmony_ci	DBG(17, "%s: end\n", __func__);
2861141cc406Sopenharmony_ci
2862141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2863141cc406Sopenharmony_ci}
2864141cc406Sopenharmony_ci
2865141cc406Sopenharmony_ciSANE_Status
2866141cc406Sopenharmony_cisane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
2867141cc406Sopenharmony_ci		    void *value, SANE_Int *info)
2868141cc406Sopenharmony_ci{
2869141cc406Sopenharmony_ci	DBG(17, "%s: action = %x, option = %d\n", __func__, action, option);
2870141cc406Sopenharmony_ci
2871141cc406Sopenharmony_ci	if (option < 0 || option >= NUM_OPTIONS)
2872141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
2873141cc406Sopenharmony_ci
2874141cc406Sopenharmony_ci	if (info != NULL)
2875141cc406Sopenharmony_ci		*info = 0;
2876141cc406Sopenharmony_ci
2877141cc406Sopenharmony_ci	switch (action) {
2878141cc406Sopenharmony_ci	case SANE_ACTION_GET_VALUE:
2879141cc406Sopenharmony_ci		return getvalue(handle, option, value);
2880141cc406Sopenharmony_ci
2881141cc406Sopenharmony_ci	case SANE_ACTION_SET_VALUE:
2882141cc406Sopenharmony_ci		return setvalue(handle, option, value, info);
2883141cc406Sopenharmony_ci
2884141cc406Sopenharmony_ci	default:
2885141cc406Sopenharmony_ci		return SANE_STATUS_INVAL;
2886141cc406Sopenharmony_ci	}
2887141cc406Sopenharmony_ci
2888141cc406Sopenharmony_ci	return SANE_STATUS_INVAL;
2889141cc406Sopenharmony_ci}
2890141cc406Sopenharmony_ci
2891141cc406Sopenharmony_ciSANE_Status
2892141cc406Sopenharmony_cisane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
2893141cc406Sopenharmony_ci{
2894141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
2895141cc406Sopenharmony_ci
2896141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
2897141cc406Sopenharmony_ci
2898141cc406Sopenharmony_ci	if (params == NULL)
2899141cc406Sopenharmony_ci		DBG(1, "%s: params is NULL\n", __func__);
2900141cc406Sopenharmony_ci
2901141cc406Sopenharmony_ci	/*
2902141cc406Sopenharmony_ci	 * If sane_start was already called, then just retrieve the parameters
2903141cc406Sopenharmony_ci	 * from the scanner data structure
2904141cc406Sopenharmony_ci	 */
2905141cc406Sopenharmony_ci
2906141cc406Sopenharmony_ci	if (!s->eof && s->ptr != NULL) {
2907141cc406Sopenharmony_ci		DBG(5, "scan in progress, returning saved params structure\n");
2908141cc406Sopenharmony_ci	} else {
2909141cc406Sopenharmony_ci		/* otherwise initialize the params structure and gather the data */
2910141cc406Sopenharmony_ci		mc_init_parameters(s);
2911141cc406Sopenharmony_ci	}
2912141cc406Sopenharmony_ci
2913141cc406Sopenharmony_ci	if (params != NULL)
2914141cc406Sopenharmony_ci		*params = s->params;
2915141cc406Sopenharmony_ci
2916141cc406Sopenharmony_ci	print_params(s->params);
2917141cc406Sopenharmony_ci
2918141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
2919141cc406Sopenharmony_ci}
2920141cc406Sopenharmony_ci
2921141cc406Sopenharmony_ci/*
2922141cc406Sopenharmony_ci * This function is part of the SANE API and gets called from the front end to
2923141cc406Sopenharmony_ci * start the scan process.
2924141cc406Sopenharmony_ci */
2925141cc406Sopenharmony_ci
2926141cc406Sopenharmony_ciSANE_Status
2927141cc406Sopenharmony_cisane_start(SANE_Handle handle)
2928141cc406Sopenharmony_ci{
2929141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
2930141cc406Sopenharmony_ci	SANE_Status status;
2931141cc406Sopenharmony_ci
2932141cc406Sopenharmony_ci	DBG(5, "%s\n", __func__);
2933141cc406Sopenharmony_ci
2934141cc406Sopenharmony_ci	/* calc scanning parameters */
2935141cc406Sopenharmony_ci	status = mc_init_parameters(s);
2936141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2937141cc406Sopenharmony_ci		return status;
2938141cc406Sopenharmony_ci
2939141cc406Sopenharmony_ci	print_params(s->params);
2940141cc406Sopenharmony_ci
2941141cc406Sopenharmony_ci	/* set scanning parameters; also query the current image
2942141cc406Sopenharmony_ci	 * parameters from the sanner and save
2943141cc406Sopenharmony_ci	 * them to s->params */
2944141cc406Sopenharmony_ci	status = mc_set_scanning_parameters(s);
2945141cc406Sopenharmony_ci
2946141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD)
2947141cc406Sopenharmony_ci		return status;
2948141cc406Sopenharmony_ci
2949141cc406Sopenharmony_ci	/* if we scan from ADF, check if it is loaded */
2950141cc406Sopenharmony_ci	if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) {
2951141cc406Sopenharmony_ci		status = mc_check_adf(s);
2952141cc406Sopenharmony_ci		if (status != SANE_STATUS_GOOD)
2953141cc406Sopenharmony_ci			return status;
2954141cc406Sopenharmony_ci	}
2955141cc406Sopenharmony_ci
2956141cc406Sopenharmony_ci	/* prepare buffer here so that a memory allocation failure
2957141cc406Sopenharmony_ci	 * will leave the scanner in a sane state.
2958141cc406Sopenharmony_ci	 */
2959141cc406Sopenharmony_ci	s->buf = realloc(s->buf, s->block_len);
2960141cc406Sopenharmony_ci	if (s->buf == NULL)
2961141cc406Sopenharmony_ci		return SANE_STATUS_NO_MEM;
2962141cc406Sopenharmony_ci
2963141cc406Sopenharmony_ci	s->eof = SANE_FALSE;
2964141cc406Sopenharmony_ci	s->ptr = s->end = s->buf;
2965141cc406Sopenharmony_ci	s->canceling = SANE_FALSE;
2966141cc406Sopenharmony_ci
2967141cc406Sopenharmony_ci	/* start scanning */
2968141cc406Sopenharmony_ci	DBG(1, "%s: scanning...\n", __func__);
2969141cc406Sopenharmony_ci
2970141cc406Sopenharmony_ci	status = mc_start_scan(s);
2971141cc406Sopenharmony_ci
2972141cc406Sopenharmony_ci	if (status != SANE_STATUS_GOOD) {
2973141cc406Sopenharmony_ci		DBG(1, "%s: start failed: %s\n", __func__,
2974141cc406Sopenharmony_ci		    sane_strstatus(status));
2975141cc406Sopenharmony_ci
2976141cc406Sopenharmony_ci		return status;
2977141cc406Sopenharmony_ci	}
2978141cc406Sopenharmony_ci
2979141cc406Sopenharmony_ci	return status;
2980141cc406Sopenharmony_ci}
2981141cc406Sopenharmony_ci
2982141cc406Sopenharmony_ci/* this moves data from our buffers to SANE */
2983141cc406Sopenharmony_ci
2984141cc406Sopenharmony_ciSANE_Status
2985141cc406Sopenharmony_cisane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length,
2986141cc406Sopenharmony_ci	  SANE_Int *length)
2987141cc406Sopenharmony_ci{
2988141cc406Sopenharmony_ci	SANE_Status status;
2989141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
2990141cc406Sopenharmony_ci
2991141cc406Sopenharmony_ci	if (s->buf == NULL || s->canceling)
2992141cc406Sopenharmony_ci		return SANE_STATUS_CANCELLED;
2993141cc406Sopenharmony_ci
2994141cc406Sopenharmony_ci	*length = 0;
2995141cc406Sopenharmony_ci
2996141cc406Sopenharmony_ci	status = mc_read(s);
2997141cc406Sopenharmony_ci
2998141cc406Sopenharmony_ci	if (status == SANE_STATUS_CANCELLED) {
2999141cc406Sopenharmony_ci		mc_scan_finish(s);
3000141cc406Sopenharmony_ci		return status;
3001141cc406Sopenharmony_ci	}
3002141cc406Sopenharmony_ci
3003141cc406Sopenharmony_ci	DBG(18, "moving data %p %p, %d (%d lines)\n",
3004141cc406Sopenharmony_ci		(void *) s->ptr, (void *) s->end,
3005141cc406Sopenharmony_ci		max_length, max_length / s->params.bytes_per_line);
3006141cc406Sopenharmony_ci
3007141cc406Sopenharmony_ci	mc_copy_image_data(s, data, max_length, length);
3008141cc406Sopenharmony_ci
3009141cc406Sopenharmony_ci	DBG(18, "%d lines read, status: %d\n",
3010141cc406Sopenharmony_ci		*length / s->params.bytes_per_line, status);
3011141cc406Sopenharmony_ci
3012141cc406Sopenharmony_ci	/* continue reading if appropriate */
3013141cc406Sopenharmony_ci	if (status == SANE_STATUS_GOOD)
3014141cc406Sopenharmony_ci		return status;
3015141cc406Sopenharmony_ci
3016141cc406Sopenharmony_ci	mc_scan_finish(s);
3017141cc406Sopenharmony_ci
3018141cc406Sopenharmony_ci	return status;
3019141cc406Sopenharmony_ci}
3020141cc406Sopenharmony_ci
3021141cc406Sopenharmony_ci/*
3022141cc406Sopenharmony_ci * void sane_cancel(SANE_Handle handle)
3023141cc406Sopenharmony_ci *
3024141cc406Sopenharmony_ci * Set the cancel flag to true. The next time the backend requests data
3025141cc406Sopenharmony_ci * from the scanner the CAN message will be sent.
3026141cc406Sopenharmony_ci */
3027141cc406Sopenharmony_ci
3028141cc406Sopenharmony_civoid
3029141cc406Sopenharmony_cisane_cancel(SANE_Handle handle)
3030141cc406Sopenharmony_ci{
3031141cc406Sopenharmony_ci	Magicolor_Scanner *s = (Magicolor_Scanner *) handle;
3032141cc406Sopenharmony_ci
3033141cc406Sopenharmony_ci	s->canceling = SANE_TRUE;
3034141cc406Sopenharmony_ci}
3035141cc406Sopenharmony_ci
3036141cc406Sopenharmony_ci/*
3037141cc406Sopenharmony_ci * SANE_Status sane_set_io_mode()
3038141cc406Sopenharmony_ci *
3039141cc406Sopenharmony_ci * not supported - for asynchronous I/O
3040141cc406Sopenharmony_ci */
3041141cc406Sopenharmony_ci
3042141cc406Sopenharmony_ciSANE_Status
3043141cc406Sopenharmony_cisane_set_io_mode(SANE_Handle __sane_unused__ handle,
3044141cc406Sopenharmony_ci	SANE_Bool __sane_unused__ non_blocking)
3045141cc406Sopenharmony_ci{
3046141cc406Sopenharmony_ci	return SANE_STATUS_UNSUPPORTED;
3047141cc406Sopenharmony_ci}
3048141cc406Sopenharmony_ci
3049141cc406Sopenharmony_ci/*
3050141cc406Sopenharmony_ci * SANE_Status sane_get_select_fd()
3051141cc406Sopenharmony_ci *
3052141cc406Sopenharmony_ci * not supported - for asynchronous I/O
3053141cc406Sopenharmony_ci */
3054141cc406Sopenharmony_ci
3055141cc406Sopenharmony_ciSANE_Status
3056141cc406Sopenharmony_cisane_get_select_fd(SANE_Handle __sane_unused__ handle,
3057141cc406Sopenharmony_ci	SANE_Int __sane_unused__ *fd)
3058141cc406Sopenharmony_ci{
3059141cc406Sopenharmony_ci	return SANE_STATUS_UNSUPPORTED;
3060141cc406Sopenharmony_ci}
3061