1/* sane - Scanner Access Now Easy.
2
3   Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
4   Copyright (C) 2005-2007 Henning Geinitz <sane@geinitz.org>
5
6   This file is part of the SANE package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
21   As a special exception, the authors of SANE give permission for
22   additional uses of the libraries contained in this release of SANE.
23
24   The exception is that, if you link a SANE library with other files
25   to produce an executable, this does not by itself cause the
26   resulting executable to be covered by the GNU General Public
27   License.  Your use of that executable is in no way restricted on
28   account of linking the SANE library code into it.
29
30   This exception does not, however, invalidate any other reasons why
31   the executable file might be covered by the GNU General Public
32   License.
33
34   If you submit changes to SANE to the maintainers to be included in
35   a subsequent release, you agree by submitting the changes that
36   those changes may be distributed with this exception intact.
37
38   If you write modifications of your own for SANE, it is your choice
39   whether to permit this exception to apply to your modifications.
40   If you do not wish that, delete this exception notice.
41*/
42
43/** @file
44 * @brief GT68xx commands common for most GT68xx-based scanners.
45 */
46
47#include "gt68xx_generic.h"
48
49
50SANE_Status
51gt68xx_generic_move_relative (GT68xx_Device * dev, SANE_Int distance)
52{
53  GT68xx_Packet req;
54
55  memset (req, 0, sizeof (req));
56  if (distance >= 0)
57    req[0] = 0x14;
58  else
59    {
60      req[0] = 0x15;
61      distance = -distance;
62    }
63  req[1] = 0x01;
64  req[2] = LOBYTE (distance);
65  req[3] = HIBYTE (distance);
66
67  return gt68xx_device_req (dev, req, req);
68}
69
70SANE_Status
71gt68xx_generic_start_scan (GT68xx_Device * dev)
72{
73  GT68xx_Packet req;
74  SANE_Status status;
75
76  memset (req, 0, sizeof (req));
77  req[0] = 0x43;
78  req[1] = 0x01;
79  RIE (gt68xx_device_req (dev, req, req));
80  RIE (gt68xx_device_check_result (req, 0x43));
81
82  return SANE_STATUS_GOOD;
83}
84
85SANE_Status
86gt68xx_generic_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready)
87{
88  SANE_Status status;
89  GT68xx_Packet req;
90
91  memset (req, 0, sizeof (req));
92  req[0] = 0x35;
93  req[1] = 0x01;
94
95  RIE (gt68xx_device_req (dev, req, req));
96
97  *ready = SANE_FALSE;
98  if (req[0] == 0)
99    *ready = SANE_TRUE;
100
101  return SANE_STATUS_GOOD;
102}
103
104static SANE_Byte
105gt68xx_generic_fix_gain (SANE_Int gain)
106{
107  if (gain < 0)
108    gain = 0;
109  else if (gain > 31)
110    gain += 12;
111  else if (gain > 51)
112    gain = 63;
113
114  return gain;
115}
116
117static SANE_Byte
118gt68xx_generic_fix_offset (SANE_Int offset)
119{
120  if (offset < 0)
121    offset = 0;
122  else if (offset > 63)
123    offset = 63;
124  return offset;
125}
126
127SANE_Status
128gt68xx_generic_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params)
129{
130  GT68xx_Packet req;
131
132  memset (req, 0, sizeof (req));
133  req[0] = 0x22;
134  req[1] = 0x01;
135  req[2] = gt68xx_generic_fix_offset (params->r_offset);
136  req[3] = gt68xx_generic_fix_gain (params->r_pga);
137  req[4] = gt68xx_generic_fix_offset (params->g_offset);
138  req[5] = gt68xx_generic_fix_gain (params->g_pga);
139  req[6] = gt68xx_generic_fix_offset (params->b_offset);
140  req[7] = gt68xx_generic_fix_gain (params->b_pga);
141
142  DBG (6,
143       "gt68xx_generic_set_afe: real AFE: 0x%02x 0x%02x  0x%02x 0x%02x  0x%02x 0x%02x\n",
144       req[2], req[3], req[4], req[5], req[6], req[7]);
145  return gt68xx_device_req (dev, req, req);
146}
147
148SANE_Status
149gt68xx_generic_set_exposure_time (GT68xx_Device * dev,
150				  GT68xx_Exposure_Parameters * params)
151{
152  GT68xx_Packet req;
153  SANE_Status status;
154
155  memset (req, 0, sizeof (req));
156  req[0] = 0x76;
157  req[1] = 0x01;
158  req[2] = req[6] = req[10] = 0x04;
159  req[4] = LOBYTE (params->r_time);
160  req[5] = HIBYTE (params->r_time);
161  req[8] = LOBYTE (params->g_time);
162  req[9] = HIBYTE (params->g_time);
163  req[12] = LOBYTE (params->b_time);
164  req[13] = HIBYTE (params->b_time);
165
166  DBG (6, "gt68xx_generic_set_exposure_time: 0x%03x 0x%03x 0x%03x\n",
167       params->r_time, params->g_time, params->b_time);
168
169  RIE (gt68xx_device_req (dev, req, req));
170  RIE (gt68xx_device_check_result (req, 0x76));
171  return SANE_STATUS_GOOD;
172}
173
174SANE_Status
175gt68xx_generic_get_id (GT68xx_Device * dev)
176{
177  GT68xx_Packet req;
178  SANE_Status status;
179
180  memset (req, 0, sizeof (req));
181  req[0] = 0x2e;
182  req[1] = 0x01;
183  RIE (gt68xx_device_req (dev, req, req));
184  RIE (gt68xx_device_check_result (req, 0x2e));
185
186  DBG (2,
187       "get_id: vendor id=0x%04X, product id=0x%04X, DID=0x%08X, FID=0x%04X\n",
188       req[2] + (req[3] << 8), req[4] + (req[5] << 8),
189       req[6] + (req[7] << 8) + (req[8] << 16) + (req[9] << 24),
190       req[10] + (req[11] << 8));
191  return SANE_STATUS_GOOD;
192}
193
194SANE_Status
195gt68xx_generic_paperfeed (GT68xx_Device * dev)
196{
197  GT68xx_Packet req;
198  SANE_Status status;
199
200  memset (req, 0, sizeof (req));
201  req[0] = 0x83;
202  req[1] = 0x01;
203
204  RIE (gt68xx_device_req (dev, req, req));
205  return SANE_STATUS_GOOD;
206}
207
208#define MAX_PIXEL_MODE 15600
209
210SANE_Status
211gt68xx_generic_setup_scan (GT68xx_Device * dev,
212			   GT68xx_Scan_Request * request,
213			   GT68xx_Scan_Action action,
214			   GT68xx_Scan_Parameters * params)
215{
216  SANE_Status status;
217  GT68xx_Model *model;
218  SANE_Int xdpi, ydpi;
219  SANE_Bool color;
220  SANE_Int depth;
221  SANE_Int pixel_x0, pixel_y0, pixel_xs, pixel_ys;
222  SANE_Int pixel_align;
223
224  SANE_Int abs_x0, abs_y0, abs_xs, abs_ys, base_xdpi, base_ydpi;
225  SANE_Int scan_xs, scan_ys, scan_bpl;
226  SANE_Int bits_per_line;
227  SANE_Byte color_mode_code;
228  SANE_Bool line_mode;
229  SANE_Int overscan_lines;
230  SANE_Fixed x0, y0, xs, ys;
231  SANE_Bool backtrack = SANE_FALSE;
232
233  DBG (6, "gt6816_setup_scan: enter (action=%s)\n",
234       action == SA_CALIBRATE ? "calibrate" :
235       action == SA_CALIBRATE_ONE_LINE ? "calibrate one line" :
236       action == SA_SCAN ? "scan" : "calculate only");
237
238  model = dev->model;
239
240  xdpi = request->xdpi;
241  ydpi = request->ydpi;
242  color = request->color;
243  depth = request->depth;
244
245  base_xdpi = model->base_xdpi;
246  base_ydpi = model->base_ydpi;
247
248  if (xdpi > model->base_xdpi)
249    base_xdpi = model->optical_xdpi;
250
251  /* Special fixes */
252  if ((dev->model->flags & GT68XX_FLAG_USE_OPTICAL_X) && xdpi <= 50)
253    base_xdpi = model->optical_xdpi;
254
255  if ((dev->model->flags & GT68XX_FLAG_SCAN_FROM_HOME) &&
256      !request->use_ta && action == SA_SCAN)
257    request->mbs = SANE_TRUE;
258
259  if (!model->constant_ydpi)
260    {
261      if (ydpi > model->base_ydpi)
262	base_ydpi = model->optical_ydpi;
263    }
264
265  DBG (6, "gt68xx_generic_setup_scan: base_xdpi=%d, base_ydpi=%d\n",
266       base_xdpi, base_ydpi);
267
268  switch (action)
269    {
270    case SA_CALIBRATE_ONE_LINE:
271      {
272	x0 = request->x0;
273	if (request->use_ta)
274	  y0 = model->y_offset_calib_ta;
275	else
276	  y0 = model->y_offset_calib;
277	ys = SANE_FIX (1.0 * MM_PER_INCH / ydpi);	/* one line */
278	xs = request->xs;
279	depth = 8;
280	break;
281      }
282    case SA_CALIBRATE:
283      {
284	if (request->use_ta)
285	  {
286	    if (dev->model->flags & GT68XX_FLAG_MIRROR_X)
287	      x0 = request->x0 - model->x_offset_ta;
288	    else
289	      x0 = request->x0 + model->x_offset_ta;
290	    if (request->mbs)
291	      y0 = model->y_offset_calib_ta;
292	    else
293	      y0 = 0;
294	  }
295	else
296	  {
297	    if (dev->model->flags & GT68XX_FLAG_MIRROR_X)
298	      x0 = request->x0 - model->x_offset;
299	    else
300	      x0 = request->x0 + model->x_offset;
301	    if (request->mbs)
302	      y0 = model->y_offset_calib;
303	    else
304	      y0 = 0;
305	  }
306	ys = SANE_FIX (CALIBRATION_HEIGHT);
307	xs = request->xs;
308	break;
309      }
310    case SA_SCAN:
311      {
312	SANE_Fixed x_offset, y_offset;
313
314	if (strcmp (dev->model->command_set->name, "mustek-gt6816") != 0)
315	  request->mbs = SANE_TRUE;	/* always go home for gt6801 scanners */
316	if (request->use_ta)
317	  {
318	    x_offset = model->x_offset_ta;
319	    if (request->mbs)
320	      y_offset = model->y_offset_ta;
321	    else
322	      {
323		y_offset = model->y_offset_ta - model->y_offset_calib_ta
324		  - SANE_FIX (CALIBRATION_HEIGHT);
325		if ((request->y0 + y_offset) < 0)
326		  {
327		    y_offset = model->y_offset_ta;
328		    request->mbs = SANE_TRUE;
329		  }
330	      }
331
332	  }
333	else
334	  {
335	    x_offset = model->x_offset;
336	    if (request->mbs)
337	      y_offset = model->y_offset;
338	    else
339	      {
340		y_offset = model->y_offset - model->y_offset_calib
341		  - SANE_FIX (CALIBRATION_HEIGHT);
342		if ((request->y0 + y_offset) < 0)
343		  {
344		    y_offset = model->y_offset;
345		    request->mbs = SANE_TRUE;
346		  }
347	      }
348
349	  }
350	if (dev->model->flags & GT68XX_FLAG_MIRROR_X)
351	  x0 = request->x0 - x_offset;
352	else
353	  x0 = request->x0 + x_offset;
354	y0 = request->y0 + y_offset;
355	if (y0 < 0)
356	  y0 = 0;
357	ys = request->ys;
358	xs = request->xs;
359	backtrack = request->backtrack;
360	break;
361      }
362
363    default:
364      DBG (1, "gt68xx_generic_setup_scan: invalid action=%d\n", (int) action);
365      return SANE_STATUS_INVAL;
366    }
367
368  pixel_x0 = SANE_UNFIX (x0) * xdpi / MM_PER_INCH + 0.5;
369  pixel_y0 = SANE_UNFIX (y0) * ydpi / MM_PER_INCH + 0.5;
370  pixel_ys = SANE_UNFIX (ys) * ydpi / MM_PER_INCH + 0.5;
371  pixel_xs = SANE_UNFIX (xs) * xdpi / MM_PER_INCH + 0.5;
372
373
374  DBG (6, "gt68xx_generic_setup_scan: xdpi=%d, ydpi=%d\n", xdpi, ydpi);
375  DBG (6, "gt68xx_generic_setup_scan: color=%s, depth=%d\n",
376       color ? "TRUE" : "FALSE", depth);
377  DBG (6, "gt68xx_generic_setup_scan: pixel_x0=%d, pixel_y0=%d\n",
378       pixel_x0, pixel_y0);
379  DBG (6, "gt68xx_generic_setup_scan: pixel_xs=%d, pixel_ys=%d\n",
380       pixel_xs, pixel_ys);
381
382
383  color_mode_code = 0x80;
384  if (color)
385    color_mode_code |= (1 << 2);
386  else
387    color_mode_code |= dev->gray_mode_color;
388
389  if (depth > 12)
390    color_mode_code |= (1 << 5);
391  else if (depth > 8)
392    {
393      color_mode_code &= 0x7f;
394      color_mode_code |= (1 << 4);
395    }
396
397  DBG (6, "gt68xx_generic_setup_scan: color_mode_code = 0x%02X\n",
398       color_mode_code);
399
400  overscan_lines = 0;
401  params->ld_shift_r = params->ld_shift_g = params->ld_shift_b = 0;
402  params->ld_shift_double = 0;
403
404  /* Line distance correction is required for color scans. */
405  if (action == SA_SCAN && color)
406    {
407      SANE_Int optical_ydpi = model->optical_ydpi;
408      SANE_Int ld_shift_r = model->ld_shift_r;
409      SANE_Int ld_shift_g = model->ld_shift_g;
410      SANE_Int ld_shift_b = model->ld_shift_b;
411      SANE_Int max_ld = MAX (MAX (ld_shift_r, ld_shift_g), ld_shift_b);
412
413      overscan_lines = max_ld * ydpi / optical_ydpi;
414      params->ld_shift_r = ld_shift_r * ydpi / optical_ydpi;
415      params->ld_shift_g = ld_shift_g * ydpi / optical_ydpi;
416      params->ld_shift_b = ld_shift_b * ydpi / optical_ydpi;
417      params->ld_shift_double = 0;
418      DBG (6, "gt68xx_generic_setup_scan: overscan=%d, ld=%d/%d/%d\n",
419	   overscan_lines, params->ld_shift_r, params->ld_shift_g,
420	   params->ld_shift_b);
421    }
422
423  /* Used for CCD scanners with 6 instead of 3 CCD lines */
424  if (action == SA_SCAN && xdpi >= model->optical_xdpi
425      && model->ld_shift_double > 0)
426    {
427      params->ld_shift_double =
428	model->ld_shift_double * ydpi / model->optical_ydpi;
429      if (color)
430	overscan_lines += (params->ld_shift_double * 3);
431      else
432	overscan_lines += params->ld_shift_double;
433
434      DBG (6, "gt68xx_generic_setup_scan: overscan=%d, ld double=%d\n",
435	   overscan_lines, params->ld_shift_double);
436    }
437
438  abs_x0 = pixel_x0 * base_xdpi / xdpi;
439  abs_y0 = pixel_y0 * base_ydpi / ydpi;
440  DBG (6, "gt68xx_generic_setup_scan: abs_x0=%d, abs_y0=%d\n", abs_x0,
441       abs_y0);
442
443  params->double_column = abs_x0 & 1;
444
445  /* Calculate minimum number of pixels which span an integral multiple of 64
446   * bytes. */
447  pixel_align = 32;		/* best case for depth = 16 */
448  while ((depth * pixel_align) % (64 * 8) != 0)
449    pixel_align *= 2;
450  DBG (6, "gt68xx_generic_setup_scan: pixel_align=%d\n", pixel_align);
451
452  if (pixel_xs % pixel_align == 0)
453    scan_xs = pixel_xs;
454  else
455    scan_xs = (pixel_xs / pixel_align + 1) * pixel_align;
456  scan_ys = pixel_ys + overscan_lines;
457
458  if ((xdpi != base_xdpi)
459      && (strcmp (dev->model->command_set->name, "mustek-gt6816") != 0))
460    abs_xs = (scan_xs - 1) * base_xdpi / xdpi;	/* gt6801 */
461  else
462    abs_xs = scan_xs * base_xdpi / xdpi;	/* gt6816 */
463
464  if (action == SA_CALIBRATE_ONE_LINE)
465    abs_ys = 2;
466  else
467    abs_ys = scan_ys * base_ydpi / ydpi;
468  DBG (6, "gt68xx_generic_setup_scan: abs_xs=%d, abs_ys=%d\n", abs_xs,
469       abs_ys);
470
471  if (model->flags & GT68XX_FLAG_NO_LINEMODE)
472    {
473      line_mode = SANE_FALSE;
474      DBG (6,
475	   "gt68xx_generic_setup_scan: using pixel mode (GT68XX_FLAG_NO_LINEMODE)\n");
476    }
477  else if (model->is_cis && !(model->flags & GT68XX_FLAG_CIS_LAMP))
478    {
479      line_mode = SANE_TRUE;
480      DBG (6, "gt68xx_generic_setup_scan: using line mode (CIS)\n");
481    }
482  else if (model->flags & GT68XX_FLAG_ALWAYS_LINEMODE)
483    {
484      line_mode = SANE_TRUE;
485      DBG (6,
486	   "gt68xx_generic_setup_scan: using line mode (GT68XX_FLAG_ALWAYS_LINEMODE)\n");
487    }
488  else
489    {
490      SANE_Int max_bpl = xdpi * 3 * depth *
491	(SANE_UNFIX (model->x_size) -
492	 SANE_UNFIX (model->x_offset)) / MM_PER_INCH / 8;
493
494      line_mode = SANE_FALSE;
495      if (!color)
496	{
497	  DBG (6,
498	       "gt68xx_generic_setup_scan: using line mode for monochrome scan\n");
499	  line_mode = SANE_TRUE;
500	}
501      else if (max_bpl > MAX_PIXEL_MODE)
502	{
503	  DBG (6,
504	       "gt68xx_generic_setup_scan: max_bpl = %d > %d: forcing line mode\n",
505	       max_bpl, MAX_PIXEL_MODE);
506	  line_mode = SANE_TRUE;
507	}
508      else
509	DBG (6,
510	     "gt68xx_generic_setup_scan: max_bpl = %d <= %d: using pixel mode\n",
511	     max_bpl, MAX_PIXEL_MODE);
512    }
513
514  bits_per_line = depth * scan_xs;
515
516  if (color && !line_mode)
517    bits_per_line *= 3;
518
519  if (bits_per_line % 8)	/* impossible */
520    {
521      DBG (0, "gt68xx_generic_setup_scan: BUG: unaligned bits_per_line=%d\n",
522	   bits_per_line);
523      return SANE_STATUS_INVAL;
524    }
525  scan_bpl = bits_per_line / 8;
526
527  if (scan_bpl % 64)		/* impossible */
528    {
529      DBG (0, "gt68xx_generic_setup_scan: BUG: unaligned scan_bpl=%d\n",
530	   scan_bpl);
531      return SANE_STATUS_INVAL;
532    }
533
534  if (color)
535    if (line_mode || dev->model->flags & GT68XX_FLAG_SE_2400)
536      scan_ys *= 3;
537
538  DBG (6, "gt68xx_generic_setup_scan: scan_xs=%d, scan_ys=%d\n", scan_xs,
539       scan_ys);
540
541  DBG (6, "gt68xx_generic_setup_scan: scan_bpl=%d\n", scan_bpl);
542
543  if (!request->calculate)
544    {
545      GT68xx_Packet req;
546      SANE_Byte motor_mode_1, motor_mode_2;
547
548      if (scan_bpl > (16 * 1024))
549	{
550	  DBG (0, "gt68xx_generic_setup_scan: scan_bpl=%d, too large\n",
551	       scan_bpl);
552	  return SANE_STATUS_NO_MEM;
553	}
554
555      if ((dev->model->flags & GT68XX_FLAG_NO_LINEMODE) && line_mode && color)
556	{
557	  DBG (0,
558	       "gt68xx_generic_setup_scan: the scanner's memory is too small for "
559	       "that combination of resolution, dpi and width\n");
560	  return SANE_STATUS_NO_MEM;
561	}
562      DBG (6, "gt68xx_generic_setup_scan: backtrack=%d\n", backtrack);
563
564      motor_mode_1 = (request->mbs ? 0 : 1) << 1;
565      motor_mode_1 |= (request->mds ? 0 : 1) << 2;
566      motor_mode_1 |= (request->mas ? 0 : 1) << 0;
567      motor_mode_1 |= (backtrack ? 1 : 0) << 3;
568
569      motor_mode_2 = (request->lamp ? 0 : 1) << 0;
570      motor_mode_2 |= (line_mode ? 0 : 1) << 2;
571
572      if ((action != SA_SCAN)
573	  && (strcmp (dev->model->command_set->name, "mustek-gt6816") == 0))
574	motor_mode_2 |= 1 << 3;
575
576      DBG (6,
577	   "gt68xx_generic_setup_scan: motor_mode_1 = 0x%02X, motor_mode_2 = 0x%02X\n",
578	   motor_mode_1, motor_mode_2);
579
580      /* Fill in the setup command */
581      memset (req, 0, sizeof (req));
582      req[0x00] = 0x20;
583      req[0x01] = 0x01;
584      req[0x02] = LOBYTE (abs_y0);
585      req[0x03] = HIBYTE (abs_y0);
586      req[0x04] = LOBYTE (abs_ys);
587      req[0x05] = HIBYTE (abs_ys);
588      req[0x06] = LOBYTE (abs_x0);
589      req[0x07] = HIBYTE (abs_x0);
590      req[0x08] = LOBYTE (abs_xs);
591      req[0x09] = HIBYTE (abs_xs);
592      req[0x0a] = color_mode_code;
593      if (model->is_cis && !(model->flags & GT68XX_FLAG_CIS_LAMP))
594	req[0x0b] = 0x60;
595      else
596	req[0x0b] = 0x20;
597      req[0x0c] = LOBYTE (xdpi);
598      req[0x0d] = HIBYTE (xdpi);
599      req[0x0e] = 0x12;		/* ??? 0x12 */
600      req[0x0f] = 0x00;		/* ??? 0x00 */
601      req[0x10] = LOBYTE (scan_bpl);
602      req[0x11] = HIBYTE (scan_bpl);
603      req[0x12] = LOBYTE (scan_ys);
604      req[0x13] = HIBYTE (scan_ys);
605      req[0x14] = motor_mode_1;
606      req[0x15] = motor_mode_2;
607      req[0x16] = LOBYTE (ydpi);
608      req[0x17] = HIBYTE (ydpi);
609      if (backtrack)
610	req[0x18] = request->backtrack_lines;
611      else
612	req[0x18] = 0x00;
613
614      status = gt68xx_device_req (dev, req, req);
615      if (status != SANE_STATUS_GOOD)
616	{
617	  DBG (3, "gt68xx_generic_setup_scan: setup request failed: %s\n",
618	       sane_strstatus (status));
619	  return status;
620	}
621      RIE (gt68xx_device_check_result (req, 0x20));
622    }
623
624  /* Fill in calculated values */
625  params->xdpi = xdpi;
626  params->ydpi = ydpi;
627  params->depth = depth;
628  params->color = color;
629  params->pixel_xs = pixel_xs;
630  params->pixel_ys = pixel_ys;
631  params->scan_xs = scan_xs;
632  params->scan_ys = scan_ys;
633  params->scan_bpl = scan_bpl;
634  params->line_mode = line_mode;
635  params->overscan_lines = overscan_lines;
636  params->pixel_x0 = pixel_x0;
637
638  DBG (6, "gt68xx_generic_setup_scan: leave: ok\n");
639  return SANE_STATUS_GOOD;
640}
641
642SANE_Status
643gt68xx_generic_move_paper (GT68xx_Device * dev,
644			   GT68xx_Scan_Request * request)
645{
646  GT68xx_Packet req;
647  SANE_Status status;
648  SANE_Int ydpi;
649  SANE_Int pixel_y0;
650  SANE_Int abs_y0, base_ydpi;
651  GT68xx_Model *model = dev->model;
652
653  ydpi = request->ydpi;
654  base_ydpi = model->base_ydpi;
655
656  if (ydpi > model->base_ydpi)
657    ydpi = base_ydpi;
658
659  pixel_y0 =
660    SANE_UNFIX ((request->y0 + model->y_offset)) * ydpi / MM_PER_INCH + 0.5;
661  abs_y0 = pixel_y0 * base_ydpi / ydpi;
662
663  DBG (6, "gt68xx_generic_move_paper: base_ydpi=%d\n", base_ydpi);
664  DBG (6, "gt68xx_generic_move_paper: ydpi=%d\n", ydpi);
665  DBG (6, "gt68xx_generic_move_paper: abs_y0=%d\n", abs_y0);
666
667  /* paper move request */
668  memset (req, 0, sizeof (req));
669  req[0] = 0x82;
670  req[1] = 0x01;
671  req[2] = LOBYTE (abs_y0);
672  req[3] = HIBYTE (abs_y0);
673  RIE (gt68xx_device_req (dev, req, req));
674
675  DBG (6, "gt68xx_generic_move_paper: leave: ok\n");
676  return SANE_STATUS_GOOD;
677}
678