1141cc406Sopenharmony_ci/*
2141cc406Sopenharmony_ci   Copyright (C) 2008, Panasonic Russia Ltd.
3141cc406Sopenharmony_ci   Copyright (C) 2010, m. allan noah
4141cc406Sopenharmony_ci*/
5141cc406Sopenharmony_ci/*
6141cc406Sopenharmony_ci   Panasonic KV-S20xx USB-SCSI scanners.
7141cc406Sopenharmony_ci*/
8141cc406Sopenharmony_ci
9141cc406Sopenharmony_ci#define DEBUG_NOT_STATIC
10141cc406Sopenharmony_ci#define BUILD 2
11141cc406Sopenharmony_ci
12141cc406Sopenharmony_ci#include "../include/sane/config.h"
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci#include <string.h>
15141cc406Sopenharmony_ci#include <unistd.h>
16141cc406Sopenharmony_ci
17141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
18141cc406Sopenharmony_ci#include "../include/sane/sanei_scsi.h"
19141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
20141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
21141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
22141cc406Sopenharmony_ci#include "../include/lassert.h"
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci#include "kvs20xx.h"
25141cc406Sopenharmony_ci#include "kvs20xx_cmd.h"
26141cc406Sopenharmony_ci
27141cc406Sopenharmony_cistruct known_device
28141cc406Sopenharmony_ci{
29141cc406Sopenharmony_ci  const SANE_Int id;
30141cc406Sopenharmony_ci  const SANE_Device scanner;
31141cc406Sopenharmony_ci};
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_cistatic const struct known_device known_devices[] = {
34141cc406Sopenharmony_ci  {
35141cc406Sopenharmony_ci    KV_S2025C,
36141cc406Sopenharmony_ci    { "", "MATSHITA", "KV-S2025C", "sheetfed scanner" },
37141cc406Sopenharmony_ci  },
38141cc406Sopenharmony_ci  {
39141cc406Sopenharmony_ci    KV_S2045C,
40141cc406Sopenharmony_ci    { "", "MATSHITA", "KV-S2045C", "sheetfed scanner" },
41141cc406Sopenharmony_ci  },
42141cc406Sopenharmony_ci  {
43141cc406Sopenharmony_ci    KV_S2026C,
44141cc406Sopenharmony_ci    { "", "MATSHITA", "KV-S2026C", "sheetfed scanner" },
45141cc406Sopenharmony_ci  },
46141cc406Sopenharmony_ci  {
47141cc406Sopenharmony_ci    KV_S2046C,
48141cc406Sopenharmony_ci    { "", "MATSHITA", "KV-S2046C", "sheetfed scanner" },
49141cc406Sopenharmony_ci  },
50141cc406Sopenharmony_ci  {
51141cc406Sopenharmony_ci    KV_S2028C,
52141cc406Sopenharmony_ci    { "", "MATSHITA", "KV-S2028C", "sheetfed scanner" },
53141cc406Sopenharmony_ci  },
54141cc406Sopenharmony_ci  {
55141cc406Sopenharmony_ci    KV_S2048C,
56141cc406Sopenharmony_ci    { "", "MATSHITA", "KV-S2048C", "sheetfed scanner" },
57141cc406Sopenharmony_ci  },
58141cc406Sopenharmony_ci};
59141cc406Sopenharmony_ci
60141cc406Sopenharmony_ciSANE_Status
61141cc406Sopenharmony_cisane_init (SANE_Int __sane_unused__ * version_code,
62141cc406Sopenharmony_ci	   SANE_Auth_Callback __sane_unused__ authorize)
63141cc406Sopenharmony_ci{
64141cc406Sopenharmony_ci  DBG_INIT ();
65141cc406Sopenharmony_ci  DBG (DBG_INFO, "This is panasonic kvs20xx driver\n");
66141cc406Sopenharmony_ci
67141cc406Sopenharmony_ci  *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);
68141cc406Sopenharmony_ci
69141cc406Sopenharmony_ci  /* Initialize USB */
70141cc406Sopenharmony_ci  sanei_usb_init ();
71141cc406Sopenharmony_ci
72141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
73141cc406Sopenharmony_ci}
74141cc406Sopenharmony_ci
75141cc406Sopenharmony_ci/*
76141cc406Sopenharmony_ci * List of available devices, allocated by sane_get_devices, released
77141cc406Sopenharmony_ci * by sane_exit()
78141cc406Sopenharmony_ci */
79141cc406Sopenharmony_cistatic SANE_Device **devlist = NULL;
80141cc406Sopenharmony_cistatic unsigned curr_scan_dev = 0;
81141cc406Sopenharmony_ci
82141cc406Sopenharmony_civoid
83141cc406Sopenharmony_cisane_exit (void)
84141cc406Sopenharmony_ci{
85141cc406Sopenharmony_ci  if (devlist)
86141cc406Sopenharmony_ci    {
87141cc406Sopenharmony_ci      int i;
88141cc406Sopenharmony_ci      for (i = 0; devlist[i]; i++)
89141cc406Sopenharmony_ci	{
90141cc406Sopenharmony_ci	  free ((void *) devlist[i]->name);
91141cc406Sopenharmony_ci	  free ((void *) devlist[i]);
92141cc406Sopenharmony_ci	}
93141cc406Sopenharmony_ci      free ((void *) devlist);
94141cc406Sopenharmony_ci      devlist = NULL;
95141cc406Sopenharmony_ci    }
96141cc406Sopenharmony_ci}
97141cc406Sopenharmony_ci
98141cc406Sopenharmony_cistatic SANE_Status
99141cc406Sopenharmony_ciattach (SANE_String_Const devname)
100141cc406Sopenharmony_ci{
101141cc406Sopenharmony_ci  int i = 0;
102141cc406Sopenharmony_ci  if (devlist)
103141cc406Sopenharmony_ci    {
104141cc406Sopenharmony_ci      for (; devlist[i]; i++);
105141cc406Sopenharmony_ci      devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1));
106141cc406Sopenharmony_ci      if (!devlist)
107141cc406Sopenharmony_ci	return SANE_STATUS_NO_MEM;
108141cc406Sopenharmony_ci    }
109141cc406Sopenharmony_ci  else
110141cc406Sopenharmony_ci    {
111141cc406Sopenharmony_ci      devlist = malloc (sizeof (SANE_Device *) * 2);
112141cc406Sopenharmony_ci      if (!devlist)
113141cc406Sopenharmony_ci	return SANE_STATUS_NO_MEM;
114141cc406Sopenharmony_ci    }
115141cc406Sopenharmony_ci  devlist[i] = malloc (sizeof (SANE_Device));
116141cc406Sopenharmony_ci  if (!devlist[i])
117141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
118141cc406Sopenharmony_ci  memcpy (devlist[i], &known_devices[curr_scan_dev].scanner,
119141cc406Sopenharmony_ci	  sizeof (SANE_Device));
120141cc406Sopenharmony_ci  devlist[i]->name = strdup (devname);
121141cc406Sopenharmony_ci  /* terminate device list with NULL entry: */
122141cc406Sopenharmony_ci  devlist[i + 1] = 0;
123141cc406Sopenharmony_ci  DBG (DBG_INFO, "%s device attached\n", devname);
124141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
125141cc406Sopenharmony_ci}
126141cc406Sopenharmony_ci
127141cc406Sopenharmony_ci/* Get device list */
128141cc406Sopenharmony_ciSANE_Status
129141cc406Sopenharmony_cisane_get_devices (const SANE_Device *** device_list,
130141cc406Sopenharmony_ci		  SANE_Bool __sane_unused__ local_only)
131141cc406Sopenharmony_ci{
132141cc406Sopenharmony_ci  if (devlist)
133141cc406Sopenharmony_ci    {
134141cc406Sopenharmony_ci      int i;
135141cc406Sopenharmony_ci      for (i = 0; devlist[i]; i++)
136141cc406Sopenharmony_ci	{
137141cc406Sopenharmony_ci	  free ((void *) devlist[i]->name);
138141cc406Sopenharmony_ci	  free ((void *) devlist[i]);
139141cc406Sopenharmony_ci	}
140141cc406Sopenharmony_ci      free ((void *) devlist);
141141cc406Sopenharmony_ci      devlist = NULL;
142141cc406Sopenharmony_ci    }
143141cc406Sopenharmony_ci
144141cc406Sopenharmony_ci  for (curr_scan_dev = 0;
145141cc406Sopenharmony_ci       curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]);
146141cc406Sopenharmony_ci       curr_scan_dev++)
147141cc406Sopenharmony_ci    {
148141cc406Sopenharmony_ci      sanei_usb_find_devices (PANASONIC_ID,
149141cc406Sopenharmony_ci			      known_devices[curr_scan_dev].id, attach);
150141cc406Sopenharmony_ci    }
151141cc406Sopenharmony_ci  for (curr_scan_dev = 0;
152141cc406Sopenharmony_ci       curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]);
153141cc406Sopenharmony_ci       curr_scan_dev++)
154141cc406Sopenharmony_ci    {
155141cc406Sopenharmony_ci      sanei_scsi_find_devices (known_devices[curr_scan_dev].scanner.vendor,
156141cc406Sopenharmony_ci			       known_devices[curr_scan_dev].scanner.model,
157141cc406Sopenharmony_ci			       NULL, -1, -1, -1, -1, attach);
158141cc406Sopenharmony_ci    }
159141cc406Sopenharmony_ci  if(device_list)
160141cc406Sopenharmony_ci    *device_list = (const SANE_Device **) devlist;
161141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
162141cc406Sopenharmony_ci}
163141cc406Sopenharmony_ci
164141cc406Sopenharmony_ci/* Open device, return the device handle */
165141cc406Sopenharmony_ciSANE_Status
166141cc406Sopenharmony_cisane_open (SANE_String_Const devname, SANE_Handle * handle)
167141cc406Sopenharmony_ci{
168141cc406Sopenharmony_ci  unsigned i, j, id = 0;
169141cc406Sopenharmony_ci  struct scanner *s;
170141cc406Sopenharmony_ci  SANE_Int h, bus;
171141cc406Sopenharmony_ci  SANE_Status st;
172141cc406Sopenharmony_ci  if (!devlist)
173141cc406Sopenharmony_ci    {
174141cc406Sopenharmony_ci      st = sane_get_devices (NULL, 0);
175141cc406Sopenharmony_ci      if (st)
176141cc406Sopenharmony_ci        return st;
177141cc406Sopenharmony_ci    }
178141cc406Sopenharmony_ci  for (i = 0; devlist[i]; i++)
179141cc406Sopenharmony_ci    {
180141cc406Sopenharmony_ci      if (!strcmp (devlist[i]->name, devname))
181141cc406Sopenharmony_ci	break;
182141cc406Sopenharmony_ci    }
183141cc406Sopenharmony_ci  if (!devlist[i])
184141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
185141cc406Sopenharmony_ci  for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++)
186141cc406Sopenharmony_ci    {
187141cc406Sopenharmony_ci      if (!strcmp (devlist[i]->model, known_devices[j].scanner.model))
188141cc406Sopenharmony_ci	{
189141cc406Sopenharmony_ci	  id = known_devices[j].id;
190141cc406Sopenharmony_ci	  break;
191141cc406Sopenharmony_ci	}
192141cc406Sopenharmony_ci    }
193141cc406Sopenharmony_ci
194141cc406Sopenharmony_ci  st = sanei_usb_open (devname, &h);
195141cc406Sopenharmony_ci  if (st == SANE_STATUS_ACCESS_DENIED)
196141cc406Sopenharmony_ci    return st;
197141cc406Sopenharmony_ci  if (st)
198141cc406Sopenharmony_ci    {
199141cc406Sopenharmony_ci      st = sanei_scsi_open (devname, &h, kvs20xx_sense_handler, NULL);
200141cc406Sopenharmony_ci      if (st)
201141cc406Sopenharmony_ci	{
202141cc406Sopenharmony_ci	  return st;
203141cc406Sopenharmony_ci	}
204141cc406Sopenharmony_ci      bus = SCSI;
205141cc406Sopenharmony_ci    }
206141cc406Sopenharmony_ci  else
207141cc406Sopenharmony_ci    {
208141cc406Sopenharmony_ci      bus = USB;
209141cc406Sopenharmony_ci      st = sanei_usb_claim_interface (h, 0);
210141cc406Sopenharmony_ci      if (st)
211141cc406Sopenharmony_ci	{
212141cc406Sopenharmony_ci	  sanei_usb_close (h);
213141cc406Sopenharmony_ci	  return st;
214141cc406Sopenharmony_ci	}
215141cc406Sopenharmony_ci    }
216141cc406Sopenharmony_ci
217141cc406Sopenharmony_ci  s = malloc (sizeof (struct scanner));
218141cc406Sopenharmony_ci  if (!s)
219141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
220141cc406Sopenharmony_ci  memset (s, 0, sizeof (struct scanner));
221141cc406Sopenharmony_ci  s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE);
222141cc406Sopenharmony_ci  if (!s->buffer)
223141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
224141cc406Sopenharmony_ci  s->file = h;
225141cc406Sopenharmony_ci  s->bus = bus;
226141cc406Sopenharmony_ci  s->id = id;
227141cc406Sopenharmony_ci  kvs20xx_init_options (s);
228141cc406Sopenharmony_ci  *handle = s;
229141cc406Sopenharmony_ci  for (i = 0; i < 3; i++)
230141cc406Sopenharmony_ci    {
231141cc406Sopenharmony_ci      st = kvs20xx_test_unit_ready (s);
232141cc406Sopenharmony_ci      if (st)
233141cc406Sopenharmony_ci	{
234141cc406Sopenharmony_ci	  if (s->bus == SCSI)
235141cc406Sopenharmony_ci	    {
236141cc406Sopenharmony_ci	      sanei_scsi_close (s->file);
237141cc406Sopenharmony_ci	      st = sanei_scsi_open (devname, &h, kvs20xx_sense_handler, NULL);
238141cc406Sopenharmony_ci	      if (st)
239141cc406Sopenharmony_ci		return st;
240141cc406Sopenharmony_ci	    }
241141cc406Sopenharmony_ci	  else
242141cc406Sopenharmony_ci	    {
243141cc406Sopenharmony_ci	      sanei_usb_release_interface (s->file, 0);
244141cc406Sopenharmony_ci	      sanei_usb_close (s->file);
245141cc406Sopenharmony_ci	      st = sanei_usb_open (devname, &h);
246141cc406Sopenharmony_ci	      if (st)
247141cc406Sopenharmony_ci		return st;
248141cc406Sopenharmony_ci	      st = sanei_usb_claim_interface (h, 0);
249141cc406Sopenharmony_ci	      if (st)
250141cc406Sopenharmony_ci		{
251141cc406Sopenharmony_ci		  sanei_usb_close (h);
252141cc406Sopenharmony_ci		  return st;
253141cc406Sopenharmony_ci		}
254141cc406Sopenharmony_ci	    }
255141cc406Sopenharmony_ci	  s->file = h;
256141cc406Sopenharmony_ci	}
257141cc406Sopenharmony_ci      else
258141cc406Sopenharmony_ci	break;
259141cc406Sopenharmony_ci    }
260141cc406Sopenharmony_ci  if (i == 3)
261141cc406Sopenharmony_ci    return SANE_STATUS_DEVICE_BUSY;
262141cc406Sopenharmony_ci
263141cc406Sopenharmony_ci  st = kvs20xx_set_timeout (s, s->val[FEED_TIMEOUT].w);
264141cc406Sopenharmony_ci  if (st)
265141cc406Sopenharmony_ci    {
266141cc406Sopenharmony_ci      sane_close (s);
267141cc406Sopenharmony_ci      return st;
268141cc406Sopenharmony_ci    }
269141cc406Sopenharmony_ci
270141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
271141cc406Sopenharmony_ci}
272141cc406Sopenharmony_ci
273141cc406Sopenharmony_ci/* Close device */
274141cc406Sopenharmony_civoid
275141cc406Sopenharmony_cisane_close (SANE_Handle handle)
276141cc406Sopenharmony_ci{
277141cc406Sopenharmony_ci  struct scanner *s = (struct scanner *) handle;
278141cc406Sopenharmony_ci  int i;
279141cc406Sopenharmony_ci  if (s->bus == USB)
280141cc406Sopenharmony_ci    {
281141cc406Sopenharmony_ci      sanei_usb_release_interface (s->file, 0);
282141cc406Sopenharmony_ci      sanei_usb_close (s->file);
283141cc406Sopenharmony_ci    }
284141cc406Sopenharmony_ci  else
285141cc406Sopenharmony_ci    sanei_scsi_close (s->file);
286141cc406Sopenharmony_ci
287141cc406Sopenharmony_ci  for (i = 1; i < NUM_OPTIONS; i++)
288141cc406Sopenharmony_ci    {
289141cc406Sopenharmony_ci      if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s)
290141cc406Sopenharmony_ci	free (s->val[i].s);
291141cc406Sopenharmony_ci    }
292141cc406Sopenharmony_ci  if (s->data)
293141cc406Sopenharmony_ci    free (s->data);
294141cc406Sopenharmony_ci  free (s->buffer);
295141cc406Sopenharmony_ci  free (s);
296141cc406Sopenharmony_ci
297141cc406Sopenharmony_ci}
298141cc406Sopenharmony_ci
299141cc406Sopenharmony_ci/* Get option descriptor */
300141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
301141cc406Sopenharmony_cisane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
302141cc406Sopenharmony_ci{
303141cc406Sopenharmony_ci  struct scanner *s = handle;
304141cc406Sopenharmony_ci
305141cc406Sopenharmony_ci  if ((unsigned) option >= NUM_OPTIONS || option < 0)
306141cc406Sopenharmony_ci    return NULL;
307141cc406Sopenharmony_ci  return s->opt + option;
308141cc406Sopenharmony_ci}
309141cc406Sopenharmony_ci
310141cc406Sopenharmony_cistatic SANE_Status
311141cc406Sopenharmony_ciwait_document (struct scanner *s)
312141cc406Sopenharmony_ci{
313141cc406Sopenharmony_ci  SANE_Status st;
314141cc406Sopenharmony_ci  int i;
315141cc406Sopenharmony_ci  if (!strcmp ("off", s->val[MANUALFEED].s))
316141cc406Sopenharmony_ci    return kvs20xx_document_exist (s);
317141cc406Sopenharmony_ci
318141cc406Sopenharmony_ci  for (i = 0; i < s->val[FEED_TIMEOUT].w; i++)
319141cc406Sopenharmony_ci    {
320141cc406Sopenharmony_ci      st = kvs20xx_document_exist (s);
321141cc406Sopenharmony_ci      if (st != SANE_STATUS_NO_DOCS)
322141cc406Sopenharmony_ci	return st;
323141cc406Sopenharmony_ci      sleep (1);
324141cc406Sopenharmony_ci    }
325141cc406Sopenharmony_ci  return SANE_STATUS_NO_DOCS;
326141cc406Sopenharmony_ci}
327141cc406Sopenharmony_ci
328141cc406Sopenharmony_ci/* Start scanning */
329141cc406Sopenharmony_ciSANE_Status
330141cc406Sopenharmony_cisane_start (SANE_Handle handle)
331141cc406Sopenharmony_ci{
332141cc406Sopenharmony_ci  struct scanner *s = (struct scanner *) handle;
333141cc406Sopenharmony_ci  SANE_Status st;
334141cc406Sopenharmony_ci  int duplex = s->val[DUPLEX].w;
335141cc406Sopenharmony_ci
336141cc406Sopenharmony_ci  if (!s->scanning)
337141cc406Sopenharmony_ci    {
338141cc406Sopenharmony_ci      unsigned dummy_length;
339141cc406Sopenharmony_ci      st = kvs20xx_test_unit_ready (s);
340141cc406Sopenharmony_ci      if (st)
341141cc406Sopenharmony_ci	return st;
342141cc406Sopenharmony_ci
343141cc406Sopenharmony_ci      st = wait_document (s);
344141cc406Sopenharmony_ci      if (st)
345141cc406Sopenharmony_ci	return st;
346141cc406Sopenharmony_ci
347141cc406Sopenharmony_ci      st = kvs20xx_reset_window (s);
348141cc406Sopenharmony_ci      if (st)
349141cc406Sopenharmony_ci	return st;
350141cc406Sopenharmony_ci      st = kvs20xx_set_window (s, SIDE_FRONT);
351141cc406Sopenharmony_ci      if (st)
352141cc406Sopenharmony_ci	return st;
353141cc406Sopenharmony_ci      if (duplex)
354141cc406Sopenharmony_ci	{
355141cc406Sopenharmony_ci	  st = kvs20xx_set_window (s, SIDE_BACK);
356141cc406Sopenharmony_ci	  if (st)
357141cc406Sopenharmony_ci	    return st;
358141cc406Sopenharmony_ci	}
359141cc406Sopenharmony_ci      st = kvs20xx_scan (s);
360141cc406Sopenharmony_ci      if (st)
361141cc406Sopenharmony_ci	return st;
362141cc406Sopenharmony_ci
363141cc406Sopenharmony_ci      st = kvs20xx_read_picture_element (s, SIDE_FRONT, &s->params);
364141cc406Sopenharmony_ci      if (st)
365141cc406Sopenharmony_ci	return st;
366141cc406Sopenharmony_ci      if (duplex)
367141cc406Sopenharmony_ci	{
368141cc406Sopenharmony_ci	  st = get_adjust_data (s, &dummy_length);
369141cc406Sopenharmony_ci	  if (st)
370141cc406Sopenharmony_ci	    return st;
371141cc406Sopenharmony_ci	}
372141cc406Sopenharmony_ci      else
373141cc406Sopenharmony_ci	{
374141cc406Sopenharmony_ci	  dummy_length = 0;
375141cc406Sopenharmony_ci	}
376141cc406Sopenharmony_ci      s->scanning = 1;
377141cc406Sopenharmony_ci      s->page = 0;
378141cc406Sopenharmony_ci      s->read = 0;
379141cc406Sopenharmony_ci      s->side = SIDE_FRONT;
380141cc406Sopenharmony_ci      sane_get_parameters (s, NULL);
381141cc406Sopenharmony_ci      s->saved_dummy_size = s->dummy_size = dummy_length
382141cc406Sopenharmony_ci	? (dummy_length * s->val[RESOLUTION].w / 1200 - 1)
383141cc406Sopenharmony_ci	* s->params.bytes_per_line : 0;
384141cc406Sopenharmony_ci      s->side_size = s->params.lines * s->params.bytes_per_line;
385141cc406Sopenharmony_ci
386141cc406Sopenharmony_ci      s->data = realloc (s->data, duplex ? s->side_size * 2 : s->side_size);
387141cc406Sopenharmony_ci      if (!s->data)
388141cc406Sopenharmony_ci	{
389141cc406Sopenharmony_ci	  s->scanning = 0;
390141cc406Sopenharmony_ci	  return SANE_STATUS_NO_MEM;
391141cc406Sopenharmony_ci	}
392141cc406Sopenharmony_ci    }
393141cc406Sopenharmony_ci
394141cc406Sopenharmony_ci  if (duplex)
395141cc406Sopenharmony_ci    {
396141cc406Sopenharmony_ci      unsigned side = SIDE_FRONT;
397141cc406Sopenharmony_ci      unsigned read, mx;
398141cc406Sopenharmony_ci      if (s->side == SIDE_FRONT && s->read == s->side_size - s->dummy_size)
399141cc406Sopenharmony_ci	{
400141cc406Sopenharmony_ci	  s->side = SIDE_BACK;
401141cc406Sopenharmony_ci	  s->read = s->dummy_size;
402141cc406Sopenharmony_ci	  s->dummy_size = 0;
403141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
404141cc406Sopenharmony_ci	}
405141cc406Sopenharmony_ci      s->read = 0;
406141cc406Sopenharmony_ci      s->dummy_size = s->saved_dummy_size;
407141cc406Sopenharmony_ci      s->side = SIDE_FRONT;
408141cc406Sopenharmony_ci      st = kvs20xx_document_exist (s);
409141cc406Sopenharmony_ci      if (st)
410141cc406Sopenharmony_ci	return st;
411141cc406Sopenharmony_ci      for (mx = s->side_size * 2; !st; mx -= read, side ^= SIDE_BACK)
412141cc406Sopenharmony_ci	st = kvs20xx_read_image_data (s, s->page, side,
413141cc406Sopenharmony_ci				      &s->data[s->side_size * 2 - mx], mx,
414141cc406Sopenharmony_ci				      &read);
415141cc406Sopenharmony_ci    }
416141cc406Sopenharmony_ci  else
417141cc406Sopenharmony_ci    {
418141cc406Sopenharmony_ci      unsigned read, mx;
419141cc406Sopenharmony_ci      s->read = 0;
420141cc406Sopenharmony_ci      st = kvs20xx_document_exist (s);
421141cc406Sopenharmony_ci      if (st)
422141cc406Sopenharmony_ci	return st;
423141cc406Sopenharmony_ci      DBG (DBG_INFO, "start: %d\n", s->page);
424141cc406Sopenharmony_ci
425141cc406Sopenharmony_ci      for (mx = s->side_size; !st; mx -= read)
426141cc406Sopenharmony_ci	st = kvs20xx_read_image_data (s, s->page, SIDE_FRONT,
427141cc406Sopenharmony_ci				      &s->data[s->side_size - mx], mx, &read);
428141cc406Sopenharmony_ci    }
429141cc406Sopenharmony_ci  if (st && st != SANE_STATUS_EOF)
430141cc406Sopenharmony_ci    {
431141cc406Sopenharmony_ci      s->scanning = 0;
432141cc406Sopenharmony_ci      return st;
433141cc406Sopenharmony_ci    }
434141cc406Sopenharmony_ci  s->page++;
435141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
436141cc406Sopenharmony_ci}
437141cc406Sopenharmony_ci
438141cc406Sopenharmony_ciinline static void
439141cc406Sopenharmony_cimemcpy24 (u8 * dest, u8 * src, unsigned size, unsigned ls)
440141cc406Sopenharmony_ci{
441141cc406Sopenharmony_ci  unsigned i;
442141cc406Sopenharmony_ci  for (i = 0; i < size; i++)
443141cc406Sopenharmony_ci    {
444141cc406Sopenharmony_ci      dest[i * 3] = src[i];
445141cc406Sopenharmony_ci      dest[i * 3 + 1] = src[i + ls];
446141cc406Sopenharmony_ci      dest[i * 3 + 2] = src[i + 2 * ls];
447141cc406Sopenharmony_ci    }
448141cc406Sopenharmony_ci}
449141cc406Sopenharmony_ci
450141cc406Sopenharmony_ciSANE_Status
451141cc406Sopenharmony_cisane_read (SANE_Handle handle, SANE_Byte * buf,
452141cc406Sopenharmony_ci	   SANE_Int max_len, SANE_Int * len)
453141cc406Sopenharmony_ci{
454141cc406Sopenharmony_ci  struct scanner *s = (struct scanner *) handle;
455141cc406Sopenharmony_ci  int duplex = s->val[DUPLEX].w;
456141cc406Sopenharmony_ci  int color = !strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_COLOR);
457141cc406Sopenharmony_ci  int rest = s->side_size - s->read - s->dummy_size;
458141cc406Sopenharmony_ci  *len = 0;
459141cc406Sopenharmony_ci
460141cc406Sopenharmony_ci  if (!s->scanning || !rest)
461141cc406Sopenharmony_ci    {
462141cc406Sopenharmony_ci      if (strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous")))
463141cc406Sopenharmony_ci	{
464141cc406Sopenharmony_ci	  if (!duplex || s->side == SIDE_BACK)
465141cc406Sopenharmony_ci	    s->scanning = 0;
466141cc406Sopenharmony_ci	}
467141cc406Sopenharmony_ci      return SANE_STATUS_EOF;
468141cc406Sopenharmony_ci    }
469141cc406Sopenharmony_ci
470141cc406Sopenharmony_ci  *len = max_len < rest ? max_len : rest;
471141cc406Sopenharmony_ci  if (duplex && (s->id == KV_S2025C
472141cc406Sopenharmony_ci		 || s->id == KV_S2026C || s->id == KV_S2028C))
473141cc406Sopenharmony_ci    {
474141cc406Sopenharmony_ci      if (color)
475141cc406Sopenharmony_ci	{
476141cc406Sopenharmony_ci	  unsigned ls = s->params.bytes_per_line;
477141cc406Sopenharmony_ci	  unsigned i, a = s->side == SIDE_FRONT ? 0 : ls / 3;
478141cc406Sopenharmony_ci	  u8 *data;
479141cc406Sopenharmony_ci	  *len = (*len / ls) * ls;
480141cc406Sopenharmony_ci	  for (i = 0, data = s->data + s->read * 2 + a;
481141cc406Sopenharmony_ci	       i < *len / ls; buf += ls, data += 2 * ls, i++)
482141cc406Sopenharmony_ci	    memcpy24 (buf, data, ls / 3, ls * 2 / 3);
483141cc406Sopenharmony_ci	}
484141cc406Sopenharmony_ci      else
485141cc406Sopenharmony_ci	{
486141cc406Sopenharmony_ci	  unsigned ls = s->params.bytes_per_line;
487141cc406Sopenharmony_ci	  unsigned i = s->side == SIDE_FRONT ? 0 : ls;
488141cc406Sopenharmony_ci	  unsigned head = ls - (s->read % ls);
489141cc406Sopenharmony_ci	  unsigned tail = (*len - head) % ls;
490141cc406Sopenharmony_ci	  unsigned lines = (*len - head) / ls;
491141cc406Sopenharmony_ci	  u8 *data = s->data + (s->read / ls) * ls * 2 + i + s->read % ls;
492141cc406Sopenharmony_ci	  assert (data <= s->data + s->side_size * 2);
493141cc406Sopenharmony_ci	  memcpy (buf, data, head);
494141cc406Sopenharmony_ci	  for (i = 0, buf += head, data += head + (head ? ls : 0);
495141cc406Sopenharmony_ci	       i < lines; buf += ls, data += ls * 2, i++)
496141cc406Sopenharmony_ci	    {
497141cc406Sopenharmony_ci	      assert (data <= s->data + s->side_size * 2);
498141cc406Sopenharmony_ci	      memcpy (buf, data, ls);
499141cc406Sopenharmony_ci	    }
500141cc406Sopenharmony_ci	  assert ((data <= s->data + s->side_size * 2) || !tail);
501141cc406Sopenharmony_ci	  memcpy (buf, data, tail);
502141cc406Sopenharmony_ci	}
503141cc406Sopenharmony_ci      s->read += *len;
504141cc406Sopenharmony_ci    }
505141cc406Sopenharmony_ci  else
506141cc406Sopenharmony_ci    {
507141cc406Sopenharmony_ci      if (color)
508141cc406Sopenharmony_ci	{
509141cc406Sopenharmony_ci	  unsigned i, ls = s->params.bytes_per_line;
510141cc406Sopenharmony_ci	  u8 *data = s->data + s->read;
511141cc406Sopenharmony_ci	  *len = (*len / ls) * ls;
512141cc406Sopenharmony_ci	  for (i = 0; i < *len / ls; buf += ls, data += ls, i++)
513141cc406Sopenharmony_ci	    memcpy24 (buf, data, ls / 3, ls / 3);
514141cc406Sopenharmony_ci	}
515141cc406Sopenharmony_ci      else
516141cc406Sopenharmony_ci	{
517141cc406Sopenharmony_ci	  memcpy (buf, s->data + s->read, *len);
518141cc406Sopenharmony_ci	}
519141cc406Sopenharmony_ci      s->read += *len;
520141cc406Sopenharmony_ci    }
521141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
522141cc406Sopenharmony_ci}
523141cc406Sopenharmony_ci
524141cc406Sopenharmony_civoid
525141cc406Sopenharmony_cisane_cancel (SANE_Handle handle)
526141cc406Sopenharmony_ci{
527141cc406Sopenharmony_ci  struct scanner *s = (struct scanner *) handle;
528141cc406Sopenharmony_ci  s->scanning = 0;
529141cc406Sopenharmony_ci}
530141cc406Sopenharmony_ci
531141cc406Sopenharmony_ciSANE_Status
532141cc406Sopenharmony_cisane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m)
533141cc406Sopenharmony_ci{
534141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
535141cc406Sopenharmony_ci}
536141cc406Sopenharmony_ci
537141cc406Sopenharmony_ciSANE_Status
538141cc406Sopenharmony_cisane_get_select_fd (SANE_Handle __sane_unused__ h,
539141cc406Sopenharmony_ci		    SANE_Int __sane_unused__ * fd)
540141cc406Sopenharmony_ci{
541141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
542141cc406Sopenharmony_ci}
543