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 
46 sm3600.c
47 
48 SANE backend master module
49 
50 (C) Marian Matthias Eichholz 2001
51 
52 Start: 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 
86 static unsigned long ulDebugMask;
87 
88 static int		num_devices;
89 static TDevice        *pdevFirst;
90 static 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 
103 Initialise SANE options
104 
105 ====================================================================== */
106 
107 typedef 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 
118 static const SANE_String_Const aScanModes[]= {  "color", "gray", "lineart",
119 						"halftone", NULL };
120 
121 static const SANE_Range rangeXmm = {
122   SANE_FIX(0),
123   SANE_FIX(220),
124   SANE_FIX(0.1) };
125 
126 static const SANE_Range rangeYmm = {
127   SANE_FIX(0),
128   SANE_FIX(300),
129   SANE_FIX(0.1) };
130 
131 #ifdef SM3600_SUPPORT_EXPOSURE
132 static const SANE_Range rangeLumi = {
133   SANE_FIX(-100.0),
134   SANE_FIX(100.0),
135   SANE_FIX(1.0) };
136 #endif
137 
138 static const SANE_Range rangeGamma = { 0, 4095, 1 };
139 
140 static const SANE_Int setResolutions[] = { 5, 75,100,200,300,600 };
141 
142 static
143 SANE_Status
InitOptions(TInstance *this)144 InitOptions(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 
328 static SANE_Status
RegisterSaneDev(TModel model, SANE_String_Const szName)329 RegisterSaneDev (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 
355 static SANE_Status
sm_usb_attach(SANE_String_Const dev_name)356 sm_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 
381 SANE_Status
sane_init(SANE_Int *version_code, SANE_Auth_Callback authCB)382 sane_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 
408 static const SANE_Device ** devlist = 0; /* only pseudo-statical */
409 
410 void
sane_exit(void)411 sane_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 
430 SANE_Status
sane_get_devices(const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)431 sane_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 
452 SANE_Status
sane_open(SANE_String_Const devicename, SANE_Handle *handle)453 sane_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 {
462 DBG(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 
490 void
sane_close(SANE_Handle handle)491 sane_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 
535 const SANE_Option_Descriptor *
sane_get_option_descriptor(SANE_Handle handle, SANE_Int iOpt)536 sane_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 
544 SANE_Status
sane_control_option(SANE_Handle handle, SANE_Int iOpt, SANE_Action action, void *pVal, SANE_Int *pnInfo)545 sane_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 
641 static SANE_Status
SetupInternalParameters(TInstance *this)642 SetupInternalParameters(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 
670 SANE_Status
sane_get_parameters(SANE_Handle handle, SANE_Parameters *p)671 sane_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 
705 SANE_Status
sane_start(SANE_Handle handle)706 sane_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 
729 SANE_Status
sane_read(SANE_Handle handle, SANE_Byte *puchBuffer, SANE_Int cchMax, SANE_Int *pcchRead)730 sane_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 
758 void
sane_cancel(SANE_Handle handle)759 sane_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 
783 SANE_Status
sane_set_io_mode(SANE_Handle h, SANE_Bool m)784 sane_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 
792 SANE_Status
sane_get_select_fd(SANE_Handle handle, SANE_Int *fd)793 sane_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