xref: /third_party/backends/backend/sm3600.c (revision 141cc406)
1/* sane - Scanner Access Now Easy.
2   (C) Marian Matthias Eichholz 2001
3
4   This file is part of the SANE package.
5
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <https://www.gnu.org/licenses/>.
18
19   As a special exception, the authors of SANE give permission for
20   additional uses of the libraries contained in this release of SANE.
21
22   The exception is that, if you link a SANE library with other files
23   to produce an executable, this does not by itself cause the
24   resulting executable to be covered by the GNU General Public
25   License.  Your use of that executable is in no way restricted on
26   account of linking the SANE library code into it.
27
28   This exception does not, however, invalidate any other reasons why
29   the executable file might be covered by the GNU General Public
30   License.
31
32   If you submit changes to SANE to the maintainers to be included in
33   a subsequent release, you agree by submitting the changes that
34   those changes may be distributed with this exception intact.
35
36   If you write modifications of your own for SANE, it is your choice
37   whether to permit this exception to apply to your modifications.
38   If you do not wish that, delete this exception notice.
39
40   This file implements SANE backend for Microtek scanners with M011 USB
41   chip like the Microtek ScanMaker 3600, 3700 and 3750. */
42
43
44/* ======================================================================
45
46sm3600.c
47
48SANE backend master module
49
50(C) Marian Matthias Eichholz 2001
51
52Start: 2.4.2001
53
54====================================================================== */
55
56#include "../include/sane/config.h"
57#include <stdlib.h>
58#include <string.h>
59#include <errno.h>
60
61#define BUILD	6
62
63#ifndef BACKEND_NAME
64#define BACKEND_NAME sm3600
65#endif
66
67#include "../include/sane/sane.h"
68#include "../include/sane/sanei.h"
69#include "../include/sane/sanei_backend.h"
70#include "../include/sane/sanei_config.h"
71#include "../include/sane/saneopts.h"
72#include "../include/sane/sanei_usb.h"
73
74#undef HAVE_LIBUSB_LEGACY
75
76/* prevent inclusion of scantool.h */
77#define SCANTOOL_H
78/* make no real function export, since we include the modules */
79#define __SM3600EXPORT__ static
80
81/* if defined, *before* sm3600.h inclusion */
82#define SM3600_SUPPORT_EXPOSURE
83
84#include "sm3600.h"
85
86static unsigned long ulDebugMask;
87
88static int		num_devices;
89static TDevice        *pdevFirst;
90static TInstance      *pinstFirst;
91
92/* ====================================================================== */
93
94#include "sm3600-scanutil.c"
95#include "sm3600-scanusb.c"
96#include "sm3600-scanmtek.c"
97#include "sm3600-homerun.c"
98#include "sm3600-gray.c"
99#include "sm3600-color.c"
100
101/* ======================================================================
102
103Initialise SANE options
104
105====================================================================== */
106
107typedef enum { optCount,
108	       optGroupMode, optMode, optResolution,
109#ifdef SM3600_SUPPORT_EXPOSURE
110	       optBrightness, optContrast,
111#endif
112	       optPreview, optGrayPreview,
113	       optGroupGeometry,optTLX, optTLY, optBRX, optBRY,
114	       optGroupEnhancement,
115	       optGammaY, optGammaR,optGammaG,optGammaB,
116	       optLast } TOptionIndex;
117
118static const SANE_String_Const aScanModes[]= {  "color", "gray", "lineart",
119						"halftone", NULL };
120
121static const SANE_Range rangeXmm = {
122  SANE_FIX(0),
123  SANE_FIX(220),
124  SANE_FIX(0.1) };
125
126static const SANE_Range rangeYmm = {
127  SANE_FIX(0),
128  SANE_FIX(300),
129  SANE_FIX(0.1) };
130
131#ifdef SM3600_SUPPORT_EXPOSURE
132static const SANE_Range rangeLumi = {
133  SANE_FIX(-100.0),
134  SANE_FIX(100.0),
135  SANE_FIX(1.0) };
136#endif
137
138static const SANE_Range rangeGamma = { 0, 4095, 1 };
139
140static const SANE_Int setResolutions[] = { 5, 75,100,200,300,600 };
141
142static
143SANE_Status
144InitOptions(TInstance *this)
145{
146  TOptionIndex iOpt;
147  if (optLast!=NUM_OPTIONS)
148    {
149      DBG(1,"NUM_OPTIONS does not fit!");
150      return SANE_STATUS_INVAL;
151    }
152  memset(this->aoptDesc,0,sizeof(this->aoptDesc));
153  memset(this->aoptVal,0,sizeof(this->aoptVal));
154  InitGammaTables(this,0,0);
155  for (iOpt=optCount; iOpt!=optLast; iOpt++)
156    {
157      static char *achNamesXY[]= {
158	SANE_NAME_SCAN_TL_X,	SANE_NAME_SCAN_TL_Y,
159	SANE_NAME_SCAN_BR_X,	SANE_NAME_SCAN_BR_Y };
160      static char *achTitlesXY[]= {
161	SANE_TITLE_SCAN_TL_X,	SANE_TITLE_SCAN_TL_Y,
162	SANE_TITLE_SCAN_BR_X,	SANE_TITLE_SCAN_BR_Y };
163      static char *achDescXY[]= {
164	SANE_DESC_SCAN_TL_X,	SANE_DESC_SCAN_TL_Y,
165	SANE_DESC_SCAN_BR_X,	SANE_DESC_SCAN_BR_Y };
166      static double afFullBed[] = { 22.0,30.0, 50.0, 80.0 }; /* TODO: calculate exactly! */
167      static const SANE_Range *aRangesXY[] = { &rangeXmm,&rangeYmm,&rangeXmm,&rangeYmm };
168      SANE_Option_Descriptor *pdesc;
169      Option_Value           *pval;
170      /* shorthands */
171      pdesc=this->aoptDesc+iOpt;
172      pval=this->aoptVal+iOpt;
173      /* default */
174      pdesc->size=sizeof(SANE_Word);
175      pdesc->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
176
177      /*
178	Some hints:
179	*every* field needs a constraint, elseway there will be a warning.
180	*/
181
182      switch (iOpt)
183	{
184	case optCount:
185	  pdesc->title  =SANE_TITLE_NUM_OPTIONS;
186	  pdesc->desc   =SANE_DESC_NUM_OPTIONS;
187	  pdesc->type   =SANE_TYPE_INT;
188	  pdesc->cap    =SANE_CAP_SOFT_DETECT;
189	  pval->w       =(SANE_Word)optLast;
190	  break;
191	case optGroupMode:
192	  pdesc->title="Mode";
193	  pdesc->desc ="";
194	  pdesc->type = SANE_TYPE_GROUP;
195	  pdesc->cap  = SANE_CAP_ADVANCED;
196	  break;
197	case optMode:
198	  pdesc->name   =SANE_NAME_SCAN_MODE;
199	  pdesc->title  =SANE_TITLE_SCAN_MODE;
200	  pdesc->desc   ="Select the scan mode";
201	  pdesc->type   =SANE_TYPE_STRING;
202	  pdesc->size   =20;
203	  pdesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
204	  pdesc->constraint.string_list = aScanModes;
205	  pval->s       = strdup(aScanModes[color]);
206	  break;
207	case optResolution:
208	  pdesc->name   =SANE_NAME_SCAN_RESOLUTION;
209	  pdesc->title  =SANE_TITLE_SCAN_RESOLUTION;
210	  pdesc->desc   =SANE_DESC_SCAN_RESOLUTION;
211	  pdesc->type   =SANE_TYPE_INT;
212	  pdesc->unit   =SANE_UNIT_DPI;
213	  pdesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
214	  pdesc->constraint.word_list = setResolutions;
215	  pval->w       =75;
216	  break;
217#ifdef SM3600_SUPPORT_EXPOSURE
218	case optBrightness:
219	  pdesc->name   =SANE_NAME_BRIGHTNESS;
220	  pdesc->title  =SANE_TITLE_BRIGHTNESS;
221	  pdesc->desc   =SANE_DESC_BRIGHTNESS;
222	  pdesc->type   =SANE_TYPE_FIXED;
223	  pdesc->unit   =SANE_UNIT_PERCENT;
224	  pdesc->constraint_type =SANE_CONSTRAINT_RANGE;
225	  pdesc->constraint.range=&rangeLumi;
226	  pval->w       =SANE_FIX(0);
227	  break;
228	case optContrast:
229	  pdesc->name   =SANE_NAME_CONTRAST;
230	  pdesc->title  =SANE_TITLE_CONTRAST;
231	  pdesc->desc   =SANE_DESC_CONTRAST;
232	  pdesc->type   =SANE_TYPE_FIXED;
233	  pdesc->unit   =SANE_UNIT_PERCENT;
234	  pdesc->constraint_type =SANE_CONSTRAINT_RANGE;
235	  pdesc->constraint.range=&rangeLumi;
236	  pval->w       =SANE_FIX(0);
237	  break;
238#endif
239	case optPreview:
240	  pdesc->name   =SANE_NAME_PREVIEW;
241	  pdesc->title  =SANE_TITLE_PREVIEW;
242	  pdesc->desc   =SANE_DESC_PREVIEW;
243	  pdesc->type   =SANE_TYPE_BOOL;
244	  pval->w       =SANE_FALSE;
245	  break;
246	case optGrayPreview:
247	  pdesc->name   =SANE_NAME_GRAY_PREVIEW;
248	  pdesc->title  =SANE_TITLE_GRAY_PREVIEW;
249	  pdesc->desc   =SANE_DESC_GRAY_PREVIEW;
250	  pdesc->type   =SANE_TYPE_BOOL;
251	  pval->w       =SANE_FALSE;
252	  break;
253	case optGroupGeometry:
254	  pdesc->title="Geometry";
255	  pdesc->desc ="";
256	  pdesc->type = SANE_TYPE_GROUP;
257	  pdesc->constraint_type=SANE_CONSTRAINT_NONE;
258	  pdesc->cap  = SANE_CAP_ADVANCED;
259	  break;
260	case optTLX: case optTLY: case optBRX: case optBRY:
261	  pdesc->name   =achNamesXY[iOpt-optTLX];
262	  pdesc->title  =achTitlesXY[iOpt-optTLX];
263	  pdesc->desc   =achDescXY[iOpt-optTLX];
264	  pdesc->type   =SANE_TYPE_FIXED;
265	  pdesc->unit   =SANE_UNIT_MM; /* arghh */
266	  pdesc->constraint_type =SANE_CONSTRAINT_RANGE;
267	  pdesc->constraint.range=aRangesXY[iOpt-optTLX];
268	  pval->w       =SANE_FIX(afFullBed[iOpt-optTLX]);
269	  break;
270	case optGroupEnhancement:
271	  pdesc->title="Enhancement";
272	  pdesc->desc ="";
273	  pdesc->type = SANE_TYPE_GROUP;
274	  pdesc->constraint_type=SANE_CONSTRAINT_NONE;
275	  pdesc->cap  = SANE_CAP_ADVANCED;
276	  break;
277	case optGammaY:
278	  pdesc->name     = SANE_NAME_GAMMA_VECTOR;
279	  pdesc->title    = SANE_TITLE_GAMMA_VECTOR;
280	  pdesc->desc     = SANE_DESC_GAMMA_VECTOR;
281	  pdesc->type     = SANE_TYPE_INT;
282	  pdesc->unit     = SANE_UNIT_NONE;
283	  pdesc->size     = 4096*sizeof(SANE_Int);
284	  pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
285	  pdesc->constraint.range = &rangeGamma;
286	  pval->wa        = this->agammaY;
287	  break;
288	case optGammaR:
289	  pdesc->name     = SANE_NAME_GAMMA_VECTOR_R;
290	  pdesc->title    = SANE_TITLE_GAMMA_VECTOR_R;
291	  pdesc->desc     = SANE_DESC_GAMMA_VECTOR_R;
292	  pdesc->type     = SANE_TYPE_INT;
293	  pdesc->unit     = SANE_UNIT_NONE;
294	  pdesc->size     = 4096*sizeof(SANE_Int);
295	  pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
296	  pdesc->constraint.range = &rangeGamma;
297	  pval->wa        = this->agammaR;
298	  break;
299	case optGammaG:
300	  pdesc->name     = SANE_NAME_GAMMA_VECTOR_G;
301	  pdesc->title    = SANE_TITLE_GAMMA_VECTOR_G;
302	  pdesc->desc     = SANE_DESC_GAMMA_VECTOR_G;
303	  pdesc->type     = SANE_TYPE_INT;
304	  pdesc->unit     = SANE_UNIT_NONE;
305	  pdesc->size     = 4096*sizeof(SANE_Int);
306	  pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
307	  pdesc->constraint.range = &rangeGamma;
308	  pval->wa        = this->agammaG;
309	  break;
310	case optGammaB:
311	  pdesc->name     = SANE_NAME_GAMMA_VECTOR_B;
312	  pdesc->title    = SANE_TITLE_GAMMA_VECTOR_B;
313	  pdesc->desc     = SANE_DESC_GAMMA_VECTOR_B;
314	  pdesc->type     = SANE_TYPE_INT;
315	  pdesc->unit     = SANE_UNIT_NONE;
316	  pdesc->size     = 4096*sizeof(SANE_Int);
317	  pdesc->constraint_type = SANE_CONSTRAINT_RANGE;
318	  pdesc->constraint.range = &rangeGamma;
319	  pval->wa        = this->agammaB;
320	  break;
321	case optLast: /* not reached */
322	  break;
323	}
324    }
325  return SANE_STATUS_GOOD;
326}
327
328static SANE_Status
329RegisterSaneDev (TModel model, SANE_String_Const szName)
330{
331  TDevice * q;
332
333  errno = 0;
334
335  q = malloc (sizeof (*q));
336  if (!q)
337    return SANE_STATUS_NO_MEM;
338
339  memset (q, 0, sizeof (*q)); /* clear every field */
340  q->szSaneName  = strdup (szName);
341  q->sane.name   = (SANE_String_Const) q->szSaneName;
342  q->sane.vendor = "Microtek";
343  q->sane.model  = "ScanMaker 3600";
344  q->sane.type   = "flatbed scanner";
345
346  q->model=model;
347
348  ++num_devices;
349  q->pNext = pdevFirst; /* link backwards */
350  pdevFirst = q;
351
352  return SANE_STATUS_GOOD;
353}
354
355static SANE_Status
356sm_usb_attach (SANE_String_Const dev_name)
357{
358  int fd;
359  SANE_Status err;
360  SANE_Word v, p;
361  TModel model;
362
363  err = sanei_usb_open(dev_name, &fd);
364  if (err)
365    return err;
366  err = sanei_usb_get_vendor_product (fd, &v, &p);
367  if (err)
368    {
369      sanei_usb_close (fd);
370      return err;
371    }
372  DBG (DEBUG_JUNK, "found dev %04X/%04X, %s\n", v, p, dev_name);
373  model = GetScannerModel (v, p);
374  if (model != unknown)
375    RegisterSaneDev (model, dev_name);
376
377  sanei_usb_close(fd);
378  return SANE_STATUS_GOOD;
379}
380
381SANE_Status
382sane_init (SANE_Int *version_code, SANE_Auth_Callback authCB)
383{
384  int                i;
385
386  DBG_INIT();
387
388  (void) authCB; /* compiler */
389
390  DBG(DEBUG_VERBOSE,"SM3600 init\n");
391  if (version_code)
392   {
393    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);
394    DBG(DEBUG_VERBOSE,"SM3600 version: %x\n",
395    	SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD));
396   }
397
398  pdevFirst=NULL;
399
400  sanei_usb_init();
401  for (i = 0; aScanners[i].idProduct; i++)
402    {
403      sanei_usb_find_devices(SCANNER_VENDOR, aScanners[i].idProduct, sm_usb_attach);
404    }
405  return SANE_STATUS_GOOD;
406}
407
408static const SANE_Device ** devlist = 0; /* only pseudo-statical */
409
410void
411sane_exit (void)
412{
413  TDevice   *dev, *pNext;
414
415  /* free all bound resources and instances */
416  while (pinstFirst)
417    sane_close((SANE_Handle)pinstFirst); /* free all resources */
418
419  /* free all device descriptors */
420  for (dev = pdevFirst; dev; dev = pNext)
421    {
422      pNext = dev->pNext;
423      free (dev->szSaneName);
424      free (dev);
425    }
426  if (devlist) free(devlist);
427  devlist=NULL;
428}
429
430SANE_Status
431sane_get_devices (const SANE_Device *** device_list,
432		  SANE_Bool __sane_unused__ local_only)
433{
434  TDevice *dev;
435  int i;
436
437  if (devlist) free (devlist);
438
439  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
440  if (!devlist)
441    return SANE_STATUS_NO_MEM;
442
443  i = 0;
444  for (dev = pdevFirst; i < num_devices; dev = dev->pNext)
445    devlist[i++] = &dev->sane;
446  devlist[i++] = 0;
447
448  *device_list = devlist;
449  return SANE_STATUS_GOOD;
450}
451
452SANE_Status
453sane_open (SANE_String_Const devicename, SANE_Handle *handle)
454{
455  TDevice    *pdev;
456  TInstance  *this;
457  DBG(DEBUG_VERBOSE,"opening %s\n",devicename);
458  if (devicename[0]) /* selected */
459    {
460      for (pdev=pdevFirst; pdev; pdev=pdev->pNext)
461{
462DBG(DEBUG_VERBOSE,"%s<>%s\n",devicename, pdev->sane.name);
463	if (!strcmp(devicename,pdev->sane.name))
464	  break;
465}
466      /* no dynamic post-registration */
467    }
468  else
469    pdev=pdevFirst;
470  if (!pdev)
471      return SANE_STATUS_INVAL;
472  this = (TInstance*) calloc(1,sizeof(TInstance));
473  if (!this) return SANE_STATUS_NO_MEM;
474
475  *handle = (SANE_Handle)this;
476
477  ResetCalibration(this); /* do not release memory */
478  this->pNext=pinstFirst; /* register open handle */
479  pinstFirst=this;
480  this->model=pdev->model; /* memorize model */
481  /* open and prepare USB scanner handle */
482
483  if (sanei_usb_open (devicename, &this->hScanner) != SANE_STATUS_GOOD)
484    return SetError (this, SANE_STATUS_IO_ERROR, "cannot open scanner device");
485
486  this->quality=fast;
487  return InitOptions(this);
488}
489
490void
491sane_close (SANE_Handle handle)
492{
493  TInstance *this,*pParent,*p;
494  this=(TInstance*)handle;
495  DBG(DEBUG_VERBOSE,"closing scanner\n");
496  if (this->hScanner)
497    {
498      if (this->state.bScanning)
499	EndScan(this);
500
501      sanei_usb_close(this->hScanner);
502      this->hScanner=-1;
503    }
504  ResetCalibration(this); /* release calibration data */
505  /* unlink active device entry */
506  pParent=NULL;
507  for (p=pinstFirst; p; p=p->pNext)
508    {
509      if (p==this) break;
510      pParent=p;
511    }
512
513  if (!p)
514    {
515      DBG(1,"invalid handle in close()\n");
516      return;
517    }
518  /* delete instance from instance list */
519  if (pParent)
520    pParent->pNext=this->pNext;
521  else
522    pinstFirst=this->pNext; /* NULL with last entry */
523  /* free resources */
524  if (this->pchPageBuffer)
525    free(this->pchPageBuffer);
526  if (this->szErrorReason)
527    {
528      DBG(DEBUG_VERBOSE,"Error status: %d, %s",
529	  this->nErrorState, this->szErrorReason);
530      free(this->szErrorReason);
531    }
532  free(this);
533}
534
535const SANE_Option_Descriptor *
536sane_get_option_descriptor (SANE_Handle handle, SANE_Int iOpt)
537{
538  TInstance *this=(TInstance*)handle;
539  if (iOpt<NUM_OPTIONS)
540    return this->aoptDesc+iOpt;
541  return NULL;
542}
543
544SANE_Status
545sane_control_option (SANE_Handle handle, SANE_Int iOpt,
546		     SANE_Action action, void *pVal,
547		     SANE_Int *pnInfo)
548{
549  SANE_Word   cap;
550  SANE_Status rc;
551  TInstance *this;
552  this=(TInstance*)handle;
553  rc=SANE_STATUS_GOOD;
554  if (pnInfo)
555    *pnInfo=0;
556
557  if (this->state.bScanning)
558    return SANE_STATUS_DEVICE_BUSY;
559  if (iOpt>=NUM_OPTIONS)
560    return SANE_STATUS_INVAL;
561
562  cap=this->aoptDesc[iOpt].cap;
563
564  switch (action)
565    {
566
567      /* ------------------------------------------------------------ */
568
569    case SANE_ACTION_GET_VALUE:
570      switch ((TOptionIndex)iOpt)
571	{
572	case optCount:
573	case optPreview:
574	case optGrayPreview:
575	case optResolution:
576#ifdef SM3600_SUPPORT_EXPOSURE
577	case optBrightness:
578	case optContrast:
579#endif
580	case optTLX: case optTLY: case optBRX: case optBRY:
581	  *(SANE_Word*)pVal = this->aoptVal[iOpt].w;
582	  break;
583	case optMode:
584	  strcpy(pVal,this->aoptVal[iOpt].s);
585	  break;
586	case optGammaY:
587	case optGammaR:
588	case optGammaG:
589	case optGammaB:
590	  DBG(DEBUG_INFO,"getting gamma\n");
591	  memcpy(pVal,this->aoptVal[iOpt].wa, this->aoptDesc[iOpt].size);
592	  break;
593	default:
594	  return SANE_STATUS_INVAL;
595	}
596      break;
597
598      /* ------------------------------------------------------------ */
599
600    case SANE_ACTION_SET_VALUE:
601      if (!SANE_OPTION_IS_SETTABLE(cap))
602	return SANE_STATUS_INVAL;
603      rc=sanei_constrain_value(this->aoptDesc+iOpt,pVal,pnInfo);
604      if (rc!=SANE_STATUS_GOOD)
605	return rc;
606      switch ((TOptionIndex)iOpt)
607	{
608	case optResolution:
609	case optTLX: case optTLY: case optBRX: case optBRY:
610          if (pnInfo) (*pnInfo) |= SANE_INFO_RELOAD_PARAMS;
611          // fall through
612	case optPreview:
613	case optGrayPreview:
614#ifdef SM3600_SUPPORT_EXPOSURE
615	case optBrightness:
616	case optContrast:
617#endif
618	  this->aoptVal[iOpt].w = *(SANE_Word*)pVal;
619	  break;
620	case optMode:
621	  if (pnInfo)
622	    (*pnInfo) |= SANE_INFO_RELOAD_PARAMS
623	      | SANE_INFO_RELOAD_OPTIONS;
624	  strcpy(this->aoptVal[iOpt].s,pVal);
625	  break;
626	case optGammaY:
627	case optGammaR:	case optGammaG:	case optGammaB:
628	  DBG(DEBUG_INFO,"setting gamma #%d\n",iOpt);
629	  memcpy(this->aoptVal[iOpt].wa, pVal, this->aoptDesc[iOpt].size);
630	  break;
631	default:
632	  return SANE_STATUS_INVAL;
633	}
634      break;
635    case SANE_ACTION_SET_AUTO:
636      return SANE_STATUS_UNSUPPORTED;
637    } /* switch action */
638  return rc; /* normally GOOD */
639}
640
641static SANE_Status
642SetupInternalParameters(TInstance *this)
643{
644  int         i;
645  this->param.res=(int)this->aoptVal[optResolution].w;
646#ifdef SM3600_SUPPORT_EXPOSURE
647  this->param.nBrightness=(int)(this->aoptVal[optBrightness].w>>SANE_FIXED_SCALE_SHIFT);
648  this->param.nContrast=(int)(this->aoptVal[optContrast].w>>SANE_FIXED_SCALE_SHIFT);
649#else
650  this->param.nBrightness=0;
651  this->param.nContrast=0;
652#endif
653  this->param.x=(int)(SANE_UNFIX(this->aoptVal[optTLX].w)*1200.0/25.4);
654  this->param.y=(int)(SANE_UNFIX(this->aoptVal[optTLY].w)*1200.0/25.4);
655  this->param.cx=(int)(SANE_UNFIX(this->aoptVal[optBRX].w-this->aoptVal[optTLX].w)*1200.0/25.4)+1;
656  this->param.cy=(int)(SANE_UNFIX(this->aoptVal[optBRY].w-this->aoptVal[optTLY].w)*1200.0/25.4)+1;
657  for (i=0; aScanModes[i]; i++)
658    if (!strcasecmp(this->aoptVal[optMode].s,aScanModes[i]))
659      {
660	this->mode=(TMode)i;
661	break;
662      }
663  DBG(DEBUG_INFO,"mode=%d, res=%d, BC=[%d,%d], xywh=[%d,%d,%d,%d]\n",
664      this->mode, this->param.res,
665      this->param.nBrightness, this->param.nContrast,
666      this->param.x,this->param.y,this->param.cx,this->param.cy);
667  return SANE_STATUS_GOOD;
668}
669
670SANE_Status
671sane_get_parameters (SANE_Handle handle, SANE_Parameters *p)
672{
673  /* extremely important for xscanimage */
674  TInstance *this;
675  this=(TInstance*)handle;
676  SetupInternalParameters(this);
677  GetAreaSize(this);
678  p->pixels_per_line=this->state.cxPixel;
679  /* TODO: we need a more stable cyPixel prediction */
680  p->lines=this->state.cyPixel;
681  p->last_frame=SANE_TRUE;
682  switch (this->mode)
683    {
684    case color:
685      p->format=SANE_FRAME_RGB;
686      p->depth=8;
687      p->bytes_per_line=p->pixels_per_line*3;
688      break;
689    case gray:
690      p->format=SANE_FRAME_GRAY;
691      p->depth=8;
692      p->bytes_per_line=p->pixels_per_line;
693      break;
694    case halftone:
695    case line:
696      p->format=SANE_FRAME_GRAY;
697      p->depth=1;
698      p->bytes_per_line=(p->pixels_per_line+7)/8;
699      break;
700    }
701  DBG(DEBUG_INFO,"getting parameters (%d,%d)...\n",p->bytes_per_line,p->lines);
702  return SANE_STATUS_GOOD;
703}
704
705SANE_Status
706sane_start (SANE_Handle handle)
707{
708  TInstance    *this;
709  SANE_Status   rc;
710  this=(TInstance*)handle;
711  DBG(DEBUG_VERBOSE,"starting scan...\n");
712  if (this->state.bScanning) return SANE_STATUS_DEVICE_BUSY;
713  rc=SetupInternalParameters(this);
714  this->state.bCanceled=false;
715  if (!rc) rc=DoInit(this); /* oopsi, we should initialise :-) */
716  if (!rc && !this->bOptSkipOriginate) rc=DoOriginate(this,true);
717  if (!rc) rc=DoJog(this,this->calibration.yMargin);
718  if (rc) return rc;
719  this->state.bEOF=false;
720  switch (this->mode)
721    {
722    case color: rc=StartScanColor(this); break;
723    default:    rc=StartScanGray(this); break;
724    }
725  if (this->state.bCanceled) return SANE_STATUS_CANCELLED;
726  return rc;
727}
728
729SANE_Status
730sane_read (SANE_Handle handle, SANE_Byte *puchBuffer,
731	   SANE_Int cchMax,
732	   SANE_Int *pcchRead)
733{
734  SANE_Status    rc;
735  TInstance     *this;
736  this=(TInstance*)handle;
737  DBG(DEBUG_INFO,"reading chunk %d...\n",(int)cchMax);
738  *pcchRead=0;
739  if (this->state.bEOF)
740    return SANE_STATUS_EOF;
741  rc=ReadChunk(this,puchBuffer,cchMax,pcchRead);
742  DBG(DEBUG_INFO,"... line %d (%d/%d)...\n",this->state.iLine,*pcchRead,rc);
743  switch (rc)
744    {
745    case SANE_STATUS_EOF:
746      this->state.bEOF=true; /* flag EOF on next read() */
747      rc=SANE_STATUS_GOOD;   /* we do not flag THIS block! */
748      break;
749    case SANE_STATUS_GOOD:
750      if (!*pcchRead) rc=SANE_STATUS_EOF;
751      break;
752    default:
753      break;
754    }
755  return rc;
756}
757
758void
759sane_cancel (SANE_Handle handle)
760{
761  TInstance *this;
762  this=(TInstance*)handle;
763  DBG(DEBUG_VERBOSE,"cancel called...\n");
764  if (this->state.bScanning)
765    {
766      this->state.bCanceled=true;
767      if (this->state.bEOF) /* regular (fast) cancel */
768	{
769	  DBG(DEBUG_INFO,"regular end cancel\n");
770	  EndScan(this);
771	  DoJog(this,-this->calibration.yMargin);
772	}
773      else
774	{
775	  /* since Xsane does not continue scanning,
776	     we cannot defer cancellation */
777	  DBG(DEBUG_INFO,"hard cancel called...\n");
778	  CancelScan(this);
779	}
780    }
781}
782
783SANE_Status
784sane_set_io_mode(SANE_Handle h, SANE_Bool m)
785{
786  (void) h;
787  if (m==SANE_TRUE) /* no non-blocking-mode */
788    return SANE_STATUS_UNSUPPORTED;
789  return SANE_STATUS_GOOD;
790}
791
792SANE_Status
793sane_get_select_fd(SANE_Handle handle, SANE_Int *fd)
794{
795  (void) handle; (void) fd;
796  return SANE_STATUS_UNSUPPORTED; /* we have no file IO */
797}
798