1 /*.............................................................................
2 * Project : SANE library for Plustek flatbed scanners.
3 *.............................................................................
4 */
5
6 /** @file plustek.c
7 * @brief SANE backend for Plustek scanner
8 *
9 * Based on Kazuhiro Sasayama previous work on
10 * plustek.[ch] file from the SANE package.<br>
11 * Original code taken from sane-0.71<br>
12 * Copyright (C) 1997 Hypercore Software Design, Ltd.<br>
13 * Also based on the work done by Rick Bronson<br>
14 * Copyright (C) 2000-2007 Gerhard Jaeger <gerhard@gjaeger.de><br>
15 *
16 * History:
17 * - 0.30 - initial version
18 * - 0.31 - no changes
19 * - 0.32 - no changes
20 * - 0.33 - no changes
21 * - 0.34 - moved some definitions and typedefs to plustek.h
22 * - 0.35 - removed Y-correction for 12000P model<br>
23 * - getting Y-size of scan area from driver
24 * - 0.36 - disabled Dropout, as this does currently not work<br>
25 * - enabled Halftone selection only for Halftone-mode<br>
26 * - made the cancel button work by using a child process during read<br>
27 * - added version code to driver interface<br>
28 * - cleaned up the code<br>
29 * - fixed sane compatibility problems<br>
30 * - added multiple device support<br>
31 * - 12bit color-depth are now available for scanimage
32 * - 0.37 - removed X/Y autocorrection, now correcting the stuff
33 * before scanning
34 * - applied Michaels' patch to solve the sane_get_parameter problem<br>
35 * - getting X-size of scan area from driver<br>
36 * - applied Michaels patch for OPT_RESOLUTION (SANE_INFO_INEXACT stuff)
37 * - 0.38 - now using the information from the driver
38 * - some minor fixes<br>
39 * - removed dropout stuff<br>
40 * - removed some warning conditions
41 * - 0.39 - added stuff to use the backend completely in user mode<br>
42 * - fixed a potential buffer problem<br>
43 * - removed function attach_one()<br>
44 * - added USB interface stuff
45 * - 0.40 - USB scanning works now
46 * - 0.41 - added some configuration stuff and also changed .conf file<br>
47 * - added call to sanei_usb_init() and sanei_lm983x_init()
48 * - 0.42 - added adjustment stuff<br>
49 * - added custom gamma tables<br>
50 * - fixed a problem with the "size-sliders"<br>
51 * - fixed a bug that causes segfault when using the autodetection for
52 * USB devices<br>
53 * - added OS/2 switch to disable the USB stuff for OS/2
54 * - 0.43 - added support for PREVIEW flag
55 * - 0.44 - added _DBG_DUMP debug level<br>
56 * - fixed a bug, that stops our lamp timer
57 * - 0.45 - added additional flags
58 * - added WIFSIGNALED to check result of child termination
59 * - changed readImage interface for USB devices
60 * - homeing of USB scanner is now working correctly
61 * - 0.46 - added plustek-usbcal.c for extra CIS device calibration
62 * based on Montys' great work
63 * - added altCalibration option
64 * - removed parallelport support --> new backend: plustek_pp
65 * - cleanup
66 * - added sanei_thread support
67 * - 0.47 - added mov-option (model override)
68 * - removed drvOpen
69 * - added call to usb_StartLampTimer, when we're using
70 * SIGALRM for lamp timer
71 * - closing now writer pipe, when reader_process is done
72 * - 0.48 - added additional options
73 * split scanmode and bit-depth
74 * - 0.49 - improved multi-device capability
75 * - tweaked some device settings
76 * - added button support
77 * - moved AFE stuff to enhanced options
78 * - 0.50 - cleanup
79 * - activated IPC stuff
80 * - added _DBG_DCALDATA for fine calibration data logging
81 * - added OPT_SPEEDUP handling
82 * - fixed constraint_type for OPT_BUTTON
83 * - 0.51 - added fine calibration caching
84 * - removed #define _PLUSTEK_USB
85 * - 0.52 - added skipDarkStrip and OPT_LOFF4DARK to frontend options
86 * - fixed batch scanning
87 *.
88 * <hr>
89 * This file is part of the SANE package.
90 *
91 * This program is free software; you can redistribute it and/or
92 * modify it under the terms of the GNU General Public License as
93 * published by the Free Software Foundation; either version 2 of the
94 * License, or (at your option) any later version.
95 *
96 * This program is distributed in the hope that it will be useful, but
97 * WITHOUT ANY WARRANTY; without even the implied warranty of
98 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
99 * General Public License for more details.
100 *
101 * You should have received a copy of the GNU General Public License
102 * along with this program. If not, see <https://www.gnu.org/licenses/>.
103 *
104 * As a special exception, the authors of SANE give permission for
105 * additional uses of the libraries contained in this release of SANE.
106 *
107 * The exception is that, if you link a SANE library with other files
108 * to produce an executable, this does not by itself cause the
109 * resulting executable to be covered by the GNU General Public
110 * License. Your use of that executable is in no way restricted on
111 * account of linking the SANE library code into it.
112 *
113 * This exception does not, however, invalidate any other reasons why
114 * the executable file might be covered by the GNU General Public
115 * License.
116 *
117 * If you submit changes to SANE to the maintainers to be included in
118 * a subsequent release, you agree by submitting the changes that
119 * those changes may be distributed with this exception intact.
120 *
121 * If you write modifications of your own for SANE, it is your choice
122 * whether to permit this exception to apply to your modifications.
123 * If you do not wish that, delete this exception notice.
124 * <hr>
125 */
126
127 /** @mainpage
128 * @verbinclude Plustek-USB.txt
129 */
130
131 #ifdef _AIX
132 # include "../include/lalloca.h"
133 #endif
134
135 #include "../include/sane/config.h"
136 #include "../include/lalloca.h"
137
138 #include <errno.h>
139 #include <fcntl.h>
140 #include <limits.h>
141 #include <signal.h>
142 #include <stdlib.h>
143 #include <string.h>
144 #include <ctype.h>
145 #include <unistd.h>
146 #include <time.h>
147 #include <math.h>
148
149 #ifdef HAVE_SYS_TIME_H
150 #include <sys/time.h>
151 #endif
152
153 #include <sys/types.h>
154 #include <sys/ioctl.h>
155
156 #include "../include/sane/sane.h"
157 #include "../include/sane/sanei.h"
158 #include "../include/sane/saneopts.h"
159
160 #define BACKEND_VERSION "0.52-12"
161
162 #define BACKEND_NAME plustek
163 #include "../include/sane/sanei_access.h"
164 #include "../include/sane/sanei_backend.h"
165 #include "../include/sane/sanei_config.h"
166 #include "../include/sane/sanei_thread.h"
167
168 #define USE_IPC
169
170 #include "plustek-usb.h"
171 #include "plustek.h"
172
173 /*********************** the debug levels ************************************/
174
175 #define _DBG_FATAL 0
176 #define _DBG_ERROR 1
177 #define _DBG_WARNING 3
178 #define _DBG_INFO 5
179 #define _DBG_PROC 7
180 #define _DBG_SANE_INIT 10
181 #define _DBG_INFO2 15
182 #define _DBG_DREGS 20
183 #define _DBG_DCALDATA 22
184 #define _DBG_DPIC 25
185 #define _DBG_READ 30
186
187 /*****************************************************************************/
188
189 #define _SECTION "[usb]"
190 #define _DEFAULT_DEVICE "auto"
191
192 /** to disable the backend... */
193
194 /* declare it here, as it's used in plustek-usbscan.c too :-( */
195 static SANE_Bool cancelRead;
196 static DevList *usbDevs;
197
198 /* the USB-stuff... I know this is in general no good idea, but it works */
199 #include "plustek-usbio.c"
200 #include "plustek-usbdevs.c"
201 #include "plustek-usbhw.c"
202 #include "plustek-usbmap.c"
203 #include "plustek-usbscan.c"
204 #include "plustek-usbimg.c"
205 #include "plustek-usbcalfile.c"
206 #include "plustek-usbshading.c"
207 #include "plustek-usbcal.c"
208 #include "plustek-usb.c"
209
210 /************************** global vars **************************************/
211
212 static int num_devices;
213 static Plustek_Device *first_dev;
214 static Plustek_Scanner *first_handle;
215 static const SANE_Device **devlist = 0;
216 static unsigned long tsecs = 0;
217 static Plustek_Scanner *sc = NULL;
218
219 static const SANE_Int bpp_lm9832_list [] = { 2, 8, 14 };
220 static const SANE_Int bpp_lm9833_list [] = { 2, 8, 16 };
221
222 static const SANE_String_Const mode_list[] =
223 {
224 SANE_VALUE_SCAN_MODE_LINEART,
225 SANE_VALUE_SCAN_MODE_GRAY,
226 SANE_VALUE_SCAN_MODE_COLOR,
227 NULL
228 };
229
230 static const SANE_String_Const source_list[] =
231 {
232 SANE_I18N("Normal"),
233 SANE_I18N("Transparency"),
234 SANE_I18N("Negative"),
235 NULL
236 };
237
238 static const SANE_Range percentage_range =
239 {
240 SANE_FIX(-100), /* minimum */
241 SANE_FIX( 100), /* maximum */
242 SANE_FIX( 1) /* quantization */
243 };
244
245 static const SANE_Range warmup_range = { -1, 999, 1 };
246 static const SANE_Range offtimer_range = { 0, 999, 1 };
247 static const SANE_Range gain_range = { -1, 63, 1 };
248 static const SANE_Range loff_range = { -1, 16363, 1 };
249
250 /* authorization stuff */
251 static SANE_Auth_Callback auth = NULL;
252
253 /* prototype... */
254 static SANE_Status local_sane_start(Plustek_Scanner *, int);
255
256 /****************************** the backend... *******************************/
257
258 #define _YN(x) (x?"yes":"no")
259
260 /** function to display the configuration options for the current device
261 * @param cnf - pointer to the configuration structure whose content should be
262 * displayed
263 */
show_cnf(CnfDef * cnf)264 static void show_cnf( CnfDef *cnf )
265 {
266 DBG( _DBG_SANE_INIT,"Device configuration:\n" );
267 DBG( _DBG_SANE_INIT,"device name : >%s<\n",cnf->devName );
268 DBG( _DBG_SANE_INIT,"USB-ID : >%s<\n",cnf->usbId );
269 DBG( _DBG_SANE_INIT,"model ovr. : %d\n", cnf->adj.mov );
270 DBG( _DBG_SANE_INIT,"warmup : %ds\n", cnf->adj.warmup );
271 DBG( _DBG_SANE_INIT,"lampOff : %d\n", cnf->adj.lampOff );
272 DBG( _DBG_SANE_INIT,"lampOffOnEnd : %s\n", _YN(cnf->adj.lampOffOnEnd ));
273 DBG( _DBG_SANE_INIT,"cacheCalData : %s\n", _YN(cnf->adj.cacheCalData ));
274 DBG( _DBG_SANE_INIT,"altCalibrate : %s\n", _YN(cnf->adj.altCalibrate ));
275 DBG( _DBG_SANE_INIT,"skipCalibr. : %s\n", _YN(cnf->adj.skipCalibration));
276 DBG( _DBG_SANE_INIT,"skipFine : %s\n", _YN(cnf->adj.skipFine ));
277 DBG( _DBG_SANE_INIT,"skipFineWhite: %s\n", _YN(cnf->adj.skipFineWhite ));
278 DBG( _DBG_SANE_INIT,"skipDarkStrip: %s\n", _YN(cnf->adj.skipDarkStrip ));
279 DBG( _DBG_SANE_INIT,"incDarkTarget: %s\n", _YN(cnf->adj.incDarkTgt ));
280 DBG( _DBG_SANE_INIT,"invertNegs. : %s\n", _YN(cnf->adj.invertNegatives));
281 DBG( _DBG_SANE_INIT,"dis.Speedup : %s\n", _YN(cnf->adj.disableSpeedup ));
282 DBG( _DBG_SANE_INIT,"pos_x : %d\n", cnf->adj.pos.x );
283 DBG( _DBG_SANE_INIT,"pos_y : %d\n", cnf->adj.pos.y );
284 DBG( _DBG_SANE_INIT,"pos_shading_y: %d\n", cnf->adj.posShadingY );
285 DBG( _DBG_SANE_INIT,"neg_x : %d\n", cnf->adj.neg.x );
286 DBG( _DBG_SANE_INIT,"neg_y : %d\n", cnf->adj.neg.y );
287 DBG( _DBG_SANE_INIT,"neg_shading_y: %d\n", cnf->adj.negShadingY );
288 DBG( _DBG_SANE_INIT,"tpa_x : %d\n", cnf->adj.tpa.x );
289 DBG( _DBG_SANE_INIT,"tpa_y : %d\n", cnf->adj.tpa.y );
290 DBG( _DBG_SANE_INIT,"tpa_shading_y: %d\n", cnf->adj.tpaShadingY );
291 DBG( _DBG_SANE_INIT,"red gain : %d\n", cnf->adj.rgain );
292 DBG( _DBG_SANE_INIT,"green gain : %d\n", cnf->adj.ggain );
293 DBG( _DBG_SANE_INIT,"blue gain : %d\n", cnf->adj.bgain );
294 DBG( _DBG_SANE_INIT,"red offset : %d\n", cnf->adj.rofs );
295 DBG( _DBG_SANE_INIT,"green offset : %d\n", cnf->adj.gofs );
296 DBG( _DBG_SANE_INIT,"blue offset : %d\n", cnf->adj.bofs );
297 DBG( _DBG_SANE_INIT,"red lampoff : %d\n", cnf->adj.rlampoff );
298 DBG( _DBG_SANE_INIT,"green lampoff: %d\n", cnf->adj.glampoff );
299 DBG( _DBG_SANE_INIT,"blue lampoff : %d\n", cnf->adj.blampoff );
300 DBG( _DBG_SANE_INIT,"red Gamma : %.2f\n",cnf->adj.rgamma );
301 DBG( _DBG_SANE_INIT,"green Gamma : %.2f\n",cnf->adj.ggamma );
302 DBG( _DBG_SANE_INIT,"blue Gamma : %.2f\n",cnf->adj.bgamma );
303 DBG( _DBG_SANE_INIT,"gray Gamma : %.2f\n",cnf->adj.graygamma );
304 DBG( _DBG_SANE_INIT,"---------------------\n" );
305 }
306
307 /** Calls the device specific stop and close functions.
308 * @param dev - pointer to the device specific structure
309 * @return The function always returns SANE_STATUS_GOOD
310 */
311 static SANE_Status
drvclose(Plustek_Device * dev)312 drvclose( Plustek_Device *dev )
313 {
314 if( dev->fd >= 0 ) {
315
316 DBG( _DBG_INFO, "drvclose()\n" );
317
318 if( 0 != tsecs ) {
319 DBG( _DBG_INFO, "TIME END 1: %lus\n", time(NULL)-tsecs);
320 }
321
322 /* don't check the return values, simply do it */
323 usbDev_stopScan( dev );
324 usbDev_close ( dev );
325 sanei_access_unlock( dev->sane.name );
326 }
327 dev->fd = -1;
328
329 return SANE_STATUS_GOOD;
330 }
331
332 /** according to the mode and source we return the corresponding scanmode and
333 * bit-depth per pixel
334 */
335 static int
getScanMode(Plustek_Scanner * scanner)336 getScanMode( Plustek_Scanner *scanner )
337 {
338 int mode;
339 int scanmode;
340
341 /* are we in TPA-mode? */
342 mode = scanner->val[OPT_MODE].w;
343 if( scanner->val[OPT_EXT_MODE].w != 0 )
344 mode += 2;
345
346 scanner->params.depth = scanner->val[OPT_BIT_DEPTH].w;
347
348 if( mode == 0 ) {
349 scanmode = COLOR_BW;
350 scanner->params.depth = 1;
351 } else if( scanner->params.depth == 8 ) {
352
353 if( mode == 1 )
354 scanmode = COLOR_256GRAY;
355 else
356 scanmode = COLOR_TRUE24;
357 } else {
358 scanner->params.depth = 16;
359 if( mode == 1 )
360 scanmode = COLOR_GRAY16;
361 else
362 scanmode = COLOR_TRUE48;
363 }
364 return scanmode;
365 }
366
367 /** return the len of the largest string in the array
368 */
369 static size_t
max_string_size(const SANE_String_Const strings[])370 max_string_size (const SANE_String_Const strings[])
371 {
372 size_t size, max_size = 0;
373 SANE_Int i;
374
375 for (i = 0; strings[i]; ++i) {
376 size = strlen (strings[i]) + 1;
377 if (size > max_size)
378 max_size = size;
379 }
380 return max_size;
381 }
382
383 /** shutdown open pipes
384 */
385 static SANE_Status
close_pipe(Plustek_Scanner * scanner)386 close_pipe( Plustek_Scanner *scanner )
387 {
388 if( scanner->r_pipe >= 0 ) {
389
390 DBG( _DBG_PROC, "close_pipe (r_pipe)\n" );
391 close( scanner->r_pipe );
392 scanner->r_pipe = -1;
393 }
394 if( scanner->w_pipe >= 0 ) {
395
396 DBG( _DBG_PROC, "close_pipe (w_pipe)\n" );
397 close( scanner->w_pipe );
398 scanner->w_pipe = -1;
399 }
400 return SANE_STATUS_EOF;
401 }
402
403 /** will be called when a child is going down
404 */
405 static void
sig_chldhandler(int signo)406 sig_chldhandler( int signo )
407 {
408 DBG( _DBG_PROC, "(SIG) Child is down (signal=%d)\n", signo );
409 if (sc) {
410 sc->calibrating = SANE_FALSE;
411 sc = NULL;
412 }
413 }
414
415 /** signal handler to kill the child process
416 */
417 static void
reader_process_sigterm_handler(int signo)418 reader_process_sigterm_handler( int signo )
419 {
420 DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo );
421 _exit( SANE_STATUS_GOOD );
422 }
423
424 static void
usb_reader_process_sigterm_handler(int signo)425 usb_reader_process_sigterm_handler( int signo )
426 {
427 DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo );
428 cancelRead = SANE_TRUE;
429 }
430
431 static void
sigalarm_handler(int signo)432 sigalarm_handler( int signo )
433 {
434 _VAR_NOT_USED( signo );
435 DBG( _DBG_PROC, "ALARM!!!\n" );
436 }
437
438 /**
439 */
440 static void
thread_entry(void)441 thread_entry(void)
442 {
443 struct SIGACTION act;
444 sigset_t ignore_set;
445
446 sigfillset ( &ignore_set );
447 sigdelset ( &ignore_set, SIGTERM );
448 #if defined (__APPLE__) && defined (__MACH__)
449 sigdelset ( &ignore_set, SIGUSR2 );
450 #endif
451 sigprocmask( SIG_SETMASK, &ignore_set, 0 );
452
453 memset(&act, 0, sizeof (act));
454 sigaction( SIGTERM, &act, 0 );
455
456 cancelRead = SANE_FALSE;
457
458 /* install the signal handler */
459 sigemptyset(&(act.sa_mask));
460 act.sa_flags = 0;
461
462 act.sa_handler = reader_process_sigterm_handler;
463 sigaction( SIGTERM, &act, 0 );
464
465 act.sa_handler = usb_reader_process_sigterm_handler;
466 sigaction( SIGUSR1, &act, 0 );
467 }
468
469 /** executed as a child process
470 * read the data from the driver and send them to the parent process
471 */
472 static int
reader_process(void * args)473 reader_process( void *args )
474 {
475 int line, lerrn;
476 unsigned char *buf;
477 unsigned long status;
478 unsigned long data_length;
479 Plustek_Scanner *scanner = (Plustek_Scanner *)args;
480 Plustek_Device *dev = scanner->hw;
481 #ifdef USE_IPC
482 IPCDef ipc;
483 #endif
484
485 if( sanei_thread_is_forked()) {
486 DBG( _DBG_PROC, "reader_process started (forked)\n" );
487 close( scanner->r_pipe );
488 scanner->r_pipe = -1;
489 } else {
490 DBG( _DBG_PROC, "reader_process started (as thread)\n" );
491 }
492
493 thread_entry();
494
495 data_length = scanner->params.lines * scanner->params.bytes_per_line;
496
497 DBG( _DBG_PROC, "reader_process:"
498 "starting to READ data (%lu bytes)\n", data_length );
499 DBG( _DBG_PROC, "buf = 0x%08lx\n", (unsigned long)scanner->buf );
500
501 if( NULL == scanner->buf ) {
502 DBG( _DBG_FATAL, "NULL Pointer !!!!\n" );
503 return SANE_STATUS_IO_ERROR;
504 }
505
506 /* prepare for scanning: speed-test, warmup, calibration */
507 buf = scanner->buf;
508 status = usbDev_Prepare( scanner->hw, buf );
509
510 #ifdef USE_IPC
511 /* prepare IPC structure */
512 memset(&ipc, 0, sizeof(ipc));
513 ipc.transferRate = DEFAULT_RATE;
514
515 if( dev->transferRate > 0 && dev->transferRate != DEFAULT_RATE )
516 ipc.transferRate = dev->transferRate;
517
518 /* write ipc back to parent in any case... */
519 write( scanner->w_pipe, &ipc, sizeof(ipc));
520 #endif
521
522 /* on success, we read all data from the driver... */
523 if( 0 == status ) {
524
525 if( !usb_InCalibrationMode(dev)) {
526
527 DBG( _DBG_INFO, "reader_process: READING....\n" );
528
529 for( line = 0; line < scanner->params.lines; line++ ) {
530
531 status = usbDev_ReadLine( scanner->hw );
532 if((int)status < 0 ) {
533 break;
534 }
535 write( scanner->w_pipe, buf, scanner->params.bytes_per_line );
536 buf += scanner->params.bytes_per_line;
537 }
538 }
539 }
540 /* on error, there's no need to clean up, as this is done by the parent */
541 lerrn = errno;
542
543 close( scanner->w_pipe );
544 scanner->w_pipe = -1;
545
546 if((int)status < 0 ) {
547 DBG( _DBG_ERROR,"reader_process: read failed, status = %i, errno %i\n",
548 (int)status, lerrn );
549 if( _E_ABORT == (int)status )
550 return SANE_STATUS_CANCELLED;
551
552 if( lerrn == EBUSY )
553 return SANE_STATUS_DEVICE_BUSY;
554
555 return SANE_STATUS_IO_ERROR;
556 }
557
558 DBG( _DBG_PROC, "reader_process: finished reading data\n" );
559 return SANE_STATUS_GOOD;
560 }
561
562 /** stop the current scan process
563 */
564 static SANE_Status
do_cancel(Plustek_Scanner * scanner,SANE_Bool closepipe)565 do_cancel( Plustek_Scanner *scanner, SANE_Bool closepipe )
566 {
567 struct SIGACTION act;
568 SANE_Pid res;
569
570 DBG( _DBG_PROC,"do_cancel\n" );
571 scanner->scanning = SANE_FALSE;
572
573 if( sanei_thread_is_valid (scanner->reader_pid) ) {
574
575 DBG( _DBG_PROC, "---- killing reader_process ----\n" );
576
577 cancelRead = SANE_TRUE;
578 scanner->calibrating = SANE_FALSE;
579
580 sigemptyset(&(act.sa_mask));
581 act.sa_flags = 0;
582
583 act.sa_handler = sigalarm_handler;
584 sigaction( SIGALRM, &act, 0 );
585
586 /* kill our child process and wait until done */
587 sanei_thread_sendsig( scanner->reader_pid, SIGUSR1 );
588
589 /* give'em 10 seconds 'til done...*/
590 alarm(10);
591 res = sanei_thread_waitpid( scanner->reader_pid, 0 );
592 alarm(0);
593
594 if( res != scanner->reader_pid ) {
595 DBG( _DBG_PROC,"sanei_thread_waitpid() failed !\n");
596
597 /* do it the hard way...*/
598 #ifdef USE_PTHREAD
599 sanei_thread_kill( scanner->reader_pid );
600 #else
601 sanei_thread_sendsig( scanner->reader_pid, SIGKILL );
602 #endif
603 }
604
605 sanei_thread_invalidate( scanner->reader_pid );
606 DBG( _DBG_PROC,"reader_process killed\n");
607 #ifndef HAVE_SETITIMER
608 usb_StartLampTimer( scanner->hw );
609 #endif
610 }
611 scanner->calibrating = SANE_FALSE;
612
613 if( SANE_TRUE == closepipe ) {
614 close_pipe( scanner );
615 }
616
617 drvclose( scanner->hw );
618
619 if( tsecs != 0 ) {
620 DBG( _DBG_INFO, "TIME END 2: %lus\n", time(NULL)-tsecs);
621 tsecs = 0;
622 }
623
624 return SANE_STATUS_CANCELLED;
625 }
626
627 /** As we support only LM9831/2/3 chips we use the same
628 * sizes for each device...
629 * @param s - pointer to the scanner specific structure
630 * @return The function always returns SANE_STATUS_GOOD
631 */
632 static SANE_Status
initGammaSettings(Plustek_Scanner * s)633 initGammaSettings( Plustek_Scanner *s )
634 {
635 int i, j, val;
636 double gamma;
637
638 s->gamma_length = 4096;
639 s->gamma_range.min = 0;
640 s->gamma_range.max = 255;
641 s->gamma_range.quant = 0;
642
643 DBG( _DBG_INFO, "Presetting Gamma tables (len=%u)\n", s->gamma_length );
644
645 /* preset the gamma maps
646 */
647 for( i = 0; i < 4; i++ ) {
648
649 switch( i ) {
650 case 1: gamma = s->hw->adj.rgamma; break;
651 case 2: gamma = s->hw->adj.ggamma; break;
652 case 3: gamma = s->hw->adj.bgamma; break;
653 default: gamma = s->hw->adj.graygamma; break;
654 }
655 DBG( _DBG_INFO, "* Channel[%u], gamma %.3f\n", i, gamma );
656
657 for( j = 0; j < s->gamma_length; j++ ) {
658
659 val = (s->gamma_range.max *
660 pow((double)j / (double)(s->gamma_length-1.0),
661 1.0 / gamma ));
662
663 if( val > s->gamma_range.max )
664 val = s->gamma_range.max;
665
666 s->gamma_table[i][j] = val;
667 }
668 }
669 DBG( _DBG_INFO, "----------------------------------\n" );
670 return SANE_STATUS_GOOD;
671 }
672
673 /** Check the gamma vectors we got back and limit if necessary
674 * @param s - pointer to the scanner specific structure
675 * @return nothing
676 */
677 static void
checkGammaSettings(Plustek_Scanner * s)678 checkGammaSettings( Plustek_Scanner *s )
679 {
680 int i, j;
681
682 DBG( _DBG_INFO, "Maps changed...\n" );
683 for( i = 0; i < 4 ; i++ ) {
684 for( j = 0; j < s->gamma_length; j++ ) {
685 if( s->gamma_table[i][j] > s->gamma_range.max ) {
686 s->gamma_table[i][j] = s->gamma_range.max;
687 }
688 }
689 }
690 }
691
692 /** initialize the options for the backend according to the device we have
693 */
694 static SANE_Status
init_options(Plustek_Scanner * s)695 init_options( Plustek_Scanner *s )
696 {
697 int i;
698 Plustek_Device *dev = s->hw;
699 AdjDef *adj = &dev->adj;
700 DCapsDef *caps = &dev->usbDev.Caps;
701
702 memset(s->opt, 0, sizeof(s->opt));
703
704 for( i = 0; i < NUM_OPTIONS; ++i ) {
705 s->opt[i].size = sizeof (SANE_Word);
706 s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
707 s->opt[i].unit = SANE_UNIT_NONE;
708 }
709
710 s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
711 s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
712 s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
713 s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
714 s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
715 s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
716 s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
717
718 /* "Scan Mode" group: */
719 s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
720 s->opt[OPT_MODE_GROUP].desc = "";
721 s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
722 s->opt[OPT_MODE_GROUP].cap = 0;
723
724 /* scan mode */
725 s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
726 s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
727 s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
728 s->opt[OPT_MODE].type = SANE_TYPE_STRING;
729 s->opt[OPT_MODE].size = max_string_size(mode_list);
730 s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
731 s->opt[OPT_MODE].constraint.string_list = mode_list;
732 s->val[OPT_MODE].w = 2; /* Color */
733
734 /* bit depth */
735 s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
736 s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
737 s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
738 s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
739 s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
740 s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
741 s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
742 if( _LM9833 == dev->usbDev.HwSetting.chip )
743 s->opt[OPT_BIT_DEPTH].constraint.word_list = bpp_lm9833_list;
744 else
745 s->opt[OPT_BIT_DEPTH].constraint.word_list = bpp_lm9832_list;
746 s->val[OPT_BIT_DEPTH].w = 8;
747
748 if (caps->workaroundFlag & _WAF_ONLY_8BIT)
749 _DISABLE(OPT_BIT_DEPTH);
750
751 /* scan source */
752 s->opt[OPT_EXT_MODE].name = SANE_NAME_SCAN_SOURCE;
753 s->opt[OPT_EXT_MODE].title = SANE_TITLE_SCAN_SOURCE;
754 s->opt[OPT_EXT_MODE].desc = SANE_DESC_SCAN_SOURCE;
755 s->opt[OPT_EXT_MODE].type = SANE_TYPE_STRING;
756 s->opt[OPT_EXT_MODE].size = max_string_size(source_list);
757 s->opt[OPT_EXT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
758 s->opt[OPT_EXT_MODE].constraint.string_list = source_list;
759 s->val[OPT_EXT_MODE].w = 0; /* Normal */
760
761 /* brightness */
762 s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
763 s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
764 s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
765 s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
766 s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
767 s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
768 s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
769 s->val[OPT_BRIGHTNESS].w = 0;
770
771 /* contrast */
772 s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
773 s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
774 s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
775 s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
776 s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
777 s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
778 s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
779 s->val[OPT_CONTRAST].w = 0;
780
781 /* resolution */
782 s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
783 s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
784 s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
785 s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
786 s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
787 s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
788 s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
789 s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
790
791 /* custom-gamma table */
792 s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
793 s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
794 s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
795 s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
796 s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
797
798 /* preview */
799 s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
800 s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
801 s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
802 s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
803 s->val[OPT_PREVIEW].w = 0;
804
805 /* "Geometry" group: */
806 s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
807 s->opt[OPT_GEOMETRY_GROUP].desc = "";
808 s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
809 s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
810
811 /* top-left x */
812 s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
813 s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
814 s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
815 s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
816 s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
817 s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
818 s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
819 s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
820
821 /* top-left y */
822 s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
823 s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
824 s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
825 s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
826 s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
827 s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
828 s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
829 s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
830
831 /* bottom-right x */
832 s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
833 s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
834 s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
835 s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
836 s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
837 s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
838 s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
839 s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
840
841 /* bottom-right y */
842 s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
843 s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
844 s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
845 s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
846 s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
847 s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
848 s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
849 s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
850
851 /* "Enhancement" group: */
852 s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement");
853 s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
854 s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
855 s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
856 s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
857
858 initGammaSettings( s );
859
860 /* grayscale gamma vector */
861 s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
862 s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
863 s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
864 s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
865 s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
866 s->val[OPT_GAMMA_VECTOR].wa = &(s->gamma_table[0][0]);
867 s->opt[OPT_GAMMA_VECTOR].constraint.range = &(s->gamma_range);
868 s->opt[OPT_GAMMA_VECTOR].size = s->gamma_length * sizeof(SANE_Word);
869
870 /* red gamma vector */
871 s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
872 s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
873 s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
874 s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
875 s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
876 s->val[OPT_GAMMA_VECTOR_R].wa = &(s->gamma_table[1][0]);
877 s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->gamma_range);
878 s->opt[OPT_GAMMA_VECTOR_R].size = s->gamma_length * sizeof(SANE_Word);
879
880 /* green gamma vector */
881 s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
882 s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
883 s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
884 s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
885 s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
886 s->val[OPT_GAMMA_VECTOR_G].wa = &(s->gamma_table[2][0]);
887 s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->gamma_range);
888 s->opt[OPT_GAMMA_VECTOR_G].size = s->gamma_length * sizeof(SANE_Word);
889
890 /* blue gamma vector */
891 s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
892 s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
893 s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
894 s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
895 s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
896 s->val[OPT_GAMMA_VECTOR_B].wa = &(s->gamma_table[3][0]);
897 s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->gamma_range);
898 s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof(SANE_Word);
899
900 /* GAMMA stuff is disabled per default */
901 _DISABLE(OPT_GAMMA_VECTOR);
902 _DISABLE(OPT_GAMMA_VECTOR_R);
903 _DISABLE(OPT_GAMMA_VECTOR_G);
904 _DISABLE(OPT_GAMMA_VECTOR_B);
905
906 /* disable extended mode list for devices without TPA */
907 if( 0 == (s->hw->caps.dwFlag & SFLAG_TPA))
908 _DISABLE(OPT_EXT_MODE);
909
910 /* "Device settings" group: */
911 s->opt[OPT_DEVICE_GROUP].title = SANE_I18N("Device-Settings");
912 s->opt[OPT_DEVICE_GROUP].desc = "";
913 s->opt[OPT_DEVICE_GROUP].type = SANE_TYPE_GROUP;
914 s->opt[OPT_DEVICE_GROUP].cap = 0;
915 s->opt[OPT_DEVICE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
916
917 s->opt[OPT_LAMPSWITCH].name = "lamp-switch";
918 s->opt[OPT_LAMPSWITCH].title = SANE_I18N("Lampswitch");;
919 s->opt[OPT_LAMPSWITCH].desc = SANE_I18N("Manually switching the lamp(s).");
920 s->opt[OPT_LAMPSWITCH].type = SANE_TYPE_BOOL;
921 s->val[OPT_LAMPSWITCH].w = SANE_FALSE;
922
923 s->opt[OPT_LOFF4DARK].name = "lamp-off-during-dcal";
924 s->opt[OPT_LOFF4DARK].title = SANE_I18N("Lamp off during dark calibration");;
925 s->opt[OPT_LOFF4DARK].desc = SANE_I18N("Always switches lamp off when doing dark calibration.");
926 s->opt[OPT_LOFF4DARK].type = SANE_TYPE_BOOL;
927 s->val[OPT_LOFF4DARK].w = adj->skipDarkStrip;
928
929 if (dev->usbDev.Caps.Normal.DarkShadOrgY < 0)
930 _DISABLE(OPT_LOFF4DARK);
931
932 s->opt[OPT_CACHECAL].name = "calibration-cache";
933 s->opt[OPT_CACHECAL].title = SANE_I18N("Calibration data cache");;
934 s->opt[OPT_CACHECAL].desc = SANE_I18N("Enables or disables calibration data cache.");
935 s->opt[OPT_CACHECAL].type = SANE_TYPE_BOOL;
936 s->val[OPT_CACHECAL].w = adj->cacheCalData;
937
938 s->opt[OPT_CALIBRATE].name = "calibrate";
939 s->opt[OPT_CALIBRATE].title = SANE_I18N("Calibrate");;
940 s->opt[OPT_CALIBRATE].desc = SANE_I18N("Performs calibration");
941 s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
942 s->opt[OPT_CALIBRATE].size = sizeof (SANE_Word);
943 s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE;
944 s->opt[OPT_CALIBRATE].constraint.range = 0;
945 s->opt[OPT_CALIBRATE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT |
946 SANE_CAP_AUTOMATIC;
947 _ENABLE(OPT_CALIBRATE);
948 if( !adj->cacheCalData )
949 _DISABLE(OPT_CALIBRATE);
950 s->val[OPT_CALIBRATE].w = 0;
951
952 /* it's currently not available for CCD devices */
953 if( !usb_IsCISDevice(dev) && !dev->adj.altCalibrate)
954 _DISABLE(OPT_CALIBRATE);
955
956 s->opt[OPT_SPEEDUP].name = "speedup-switch";
957 s->opt[OPT_SPEEDUP].title = SANE_I18N("Speedup sensor");;
958 s->opt[OPT_SPEEDUP].desc = SANE_I18N("Enables or disables speeding up sensor movement.");
959 s->opt[OPT_SPEEDUP].type = SANE_TYPE_BOOL;
960 s->val[OPT_SPEEDUP].w = !(adj->disableSpeedup);
961
962 if( s->hw->usbDev.HwSetting.dHighSpeed == 0.0 )
963 _DISABLE(OPT_SPEEDUP);
964
965 s->opt[OPT_LAMPOFF_ONEND].name = SANE_NAME_LAMP_OFF_AT_EXIT;
966 s->opt[OPT_LAMPOFF_ONEND].title = SANE_TITLE_LAMP_OFF_AT_EXIT;
967 s->opt[OPT_LAMPOFF_ONEND].desc = SANE_DESC_LAMP_OFF_AT_EXIT;
968 s->opt[OPT_LAMPOFF_ONEND].type = SANE_TYPE_BOOL;
969 s->val[OPT_LAMPOFF_ONEND].w = adj->lampOffOnEnd;
970
971 s->opt[OPT_WARMUPTIME].name = "warmup-time";
972 s->opt[OPT_WARMUPTIME].title = SANE_I18N("Warmup-time");;
973 s->opt[OPT_WARMUPTIME].desc = SANE_I18N("Warmup-time in seconds.");
974 s->opt[OPT_WARMUPTIME].type = SANE_TYPE_INT;
975 s->opt[OPT_WARMUPTIME].constraint_type = SANE_CONSTRAINT_RANGE;
976 s->opt[OPT_WARMUPTIME].constraint.range = &warmup_range;
977 s->val[OPT_WARMUPTIME].w = adj->warmup;
978 /* only available for CCD devices*/
979 if( usb_IsCISDevice( dev )) {
980 _DISABLE(OPT_WARMUPTIME);
981 s->val[OPT_WARMUPTIME].w = 0;
982 }
983
984 s->opt[OPT_LAMPOFF_TIMER].name = "lampoff-time";
985 s->opt[OPT_LAMPOFF_TIMER].title = SANE_I18N("Lampoff-time");;
986 s->opt[OPT_LAMPOFF_TIMER].desc = SANE_I18N("Lampoff-time in seconds.");
987 s->opt[OPT_LAMPOFF_TIMER].type = SANE_TYPE_INT;
988 s->opt[OPT_LAMPOFF_TIMER].constraint_type = SANE_CONSTRAINT_RANGE;
989 s->opt[OPT_LAMPOFF_TIMER].constraint.range = &offtimer_range;
990 s->val[OPT_LAMPOFF_TIMER].w = adj->lampOff;
991
992 /* "Analog Frontend" group*/
993 s->opt[OPT_AFE_GROUP].title = SANE_I18N("Analog frontend");
994 s->opt[OPT_AFE_GROUP].desc = "";
995 s->opt[OPT_AFE_GROUP].type = SANE_TYPE_GROUP;
996 s->opt[OPT_AFE_GROUP].cap = SANE_CAP_ADVANCED;
997
998 s->opt[OPT_OVR_REDGAIN].name = "red-gain";
999 s->opt[OPT_OVR_REDGAIN].title = SANE_I18N("Red gain");
1000 s->opt[OPT_OVR_REDGAIN].desc = SANE_I18N("Red gain value of the AFE");
1001 s->opt[OPT_OVR_REDGAIN].type = SANE_TYPE_INT;
1002 s->opt[OPT_OVR_REDGAIN].constraint_type = SANE_CONSTRAINT_RANGE;
1003 s->opt[OPT_OVR_REDGAIN].constraint.range = &gain_range;
1004 s->val[OPT_OVR_REDGAIN].w = adj->rgain;
1005
1006 s->opt[OPT_OVR_REDOFS].name = "red-offset";
1007 s->opt[OPT_OVR_REDOFS].title = SANE_I18N("Red offset");
1008 s->opt[OPT_OVR_REDOFS].desc = SANE_I18N("Red offset value of the AFE");
1009 s->opt[OPT_OVR_REDOFS].type = SANE_TYPE_INT;
1010 s->opt[OPT_OVR_REDOFS].constraint_type = SANE_CONSTRAINT_RANGE;
1011 s->opt[OPT_OVR_REDOFS].constraint.range = &gain_range;
1012 s->val[OPT_OVR_REDOFS].w = adj->rofs;
1013
1014 s->opt[OPT_OVR_GREENGAIN].name = "green-gain";
1015 s->opt[OPT_OVR_GREENGAIN].title = SANE_I18N("Green gain");
1016 s->opt[OPT_OVR_GREENGAIN].desc = SANE_I18N("Green gain value of the AFE");
1017 s->opt[OPT_OVR_GREENGAIN].type = SANE_TYPE_INT;
1018 s->opt[OPT_OVR_GREENGAIN].constraint_type = SANE_CONSTRAINT_RANGE;
1019 s->opt[OPT_OVR_GREENGAIN].constraint.range = &gain_range;
1020 s->val[OPT_OVR_GREENGAIN].w = adj->ggain;
1021
1022 s->opt[OPT_OVR_GREENOFS].name = "green-offset";
1023 s->opt[OPT_OVR_GREENOFS].title = SANE_I18N("Green offset");
1024 s->opt[OPT_OVR_GREENOFS].desc = SANE_I18N("Green offset value of the AFE");
1025 s->opt[OPT_OVR_GREENOFS].type = SANE_TYPE_INT;
1026 s->opt[OPT_OVR_GREENOFS].constraint_type = SANE_CONSTRAINT_RANGE;
1027 s->opt[OPT_OVR_GREENOFS].constraint.range = &gain_range;
1028 s->val[OPT_OVR_GREENOFS].w = adj->gofs;
1029
1030 s->opt[OPT_OVR_BLUEGAIN].name = "blue-gain";
1031 s->opt[OPT_OVR_BLUEGAIN].title = SANE_I18N("Blue gain");
1032 s->opt[OPT_OVR_BLUEGAIN].desc = SANE_I18N("Blue gain value of the AFE");
1033 s->opt[OPT_OVR_BLUEGAIN].type = SANE_TYPE_INT;
1034 s->opt[OPT_OVR_BLUEGAIN].constraint_type = SANE_CONSTRAINT_RANGE;
1035 s->opt[OPT_OVR_BLUEGAIN].constraint.range = &gain_range;
1036 s->val[OPT_OVR_BLUEGAIN].w = adj->bgain;
1037
1038 s->opt[OPT_OVR_BLUEOFS].name = "blue-offset";
1039 s->opt[OPT_OVR_BLUEOFS].title = SANE_I18N("Blue offset");
1040 s->opt[OPT_OVR_BLUEOFS].desc = SANE_I18N("Blue offset value of the AFE");
1041 s->opt[OPT_OVR_BLUEOFS].type = SANE_TYPE_INT;
1042 s->opt[OPT_OVR_BLUEOFS].constraint_type = SANE_CONSTRAINT_RANGE;
1043 s->opt[OPT_OVR_BLUEOFS].constraint.range = &gain_range;
1044 s->val[OPT_OVR_BLUEOFS].w = adj->bofs;
1045
1046 s->opt[OPT_OVR_RED_LOFF].name = "redlamp-off";
1047 s->opt[OPT_OVR_RED_LOFF].title = SANE_I18N("Red lamp off");
1048 s->opt[OPT_OVR_RED_LOFF].desc = SANE_I18N("Defines red lamp off parameter");
1049 s->opt[OPT_OVR_RED_LOFF].type = SANE_TYPE_INT;
1050 s->opt[OPT_OVR_RED_LOFF].constraint_type = SANE_CONSTRAINT_RANGE;
1051 s->opt[OPT_OVR_RED_LOFF].constraint.range = &loff_range;
1052 s->val[OPT_OVR_RED_LOFF].w = adj->rlampoff;
1053
1054 s->opt[OPT_OVR_GREEN_LOFF].name = "greenlamp-off";
1055 s->opt[OPT_OVR_GREEN_LOFF].title = SANE_I18N("Green lamp off");
1056 s->opt[OPT_OVR_GREEN_LOFF].desc = SANE_I18N("Defines green lamp off parameter");
1057 s->opt[OPT_OVR_GREEN_LOFF].type = SANE_TYPE_INT;
1058 s->opt[OPT_OVR_GREEN_LOFF].constraint_type = SANE_CONSTRAINT_RANGE;
1059 s->opt[OPT_OVR_GREEN_LOFF].constraint.range = &loff_range;
1060 s->val[OPT_OVR_GREEN_LOFF].w = adj->glampoff;
1061
1062 s->opt[OPT_OVR_BLUE_LOFF].name = "bluelamp-off";
1063 s->opt[OPT_OVR_BLUE_LOFF].title = SANE_I18N("Blue lamp off");
1064 s->opt[OPT_OVR_BLUE_LOFF].desc = SANE_I18N("Defines blue lamp off parameter");
1065 s->opt[OPT_OVR_BLUE_LOFF].type = SANE_TYPE_INT;
1066 s->opt[OPT_OVR_BLUE_LOFF].constraint_type = SANE_CONSTRAINT_RANGE;
1067 s->opt[OPT_OVR_BLUE_LOFF].constraint.range = &loff_range;
1068 s->val[OPT_OVR_BLUE_LOFF].w = adj->blampoff;
1069
1070 /* only available for CIS devices*/
1071 if( !usb_IsCISDevice( dev )) {
1072 _DISABLE(OPT_OVR_RED_LOFF);
1073 _DISABLE(OPT_OVR_GREEN_LOFF);
1074 _DISABLE(OPT_OVR_BLUE_LOFF);
1075 }
1076
1077 /* "Button" group*/
1078 s->opt[OPT_BUTTON_GROUP].title = SANE_I18N("Buttons");
1079 s->opt[OPT_BUTTON_GROUP].desc = "";
1080 s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
1081 s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED;
1082
1083 /* scanner buttons */
1084 for( i = OPT_BUTTON_0; i <= OPT_BUTTON_LAST; i++ ) {
1085
1086 char buf [128];
1087
1088 snprintf (buf, sizeof(buf), "button %d", i - OPT_BUTTON_0);
1089 s->opt[i].name = strdup(buf);
1090
1091 snprintf (buf, sizeof(buf), "Scanner button %d", i - OPT_BUTTON_0);
1092 s->opt[i].title = strdup(buf);
1093
1094 s->opt[i].desc = SANE_I18N("This option reflects the status "
1095 "of the scanner buttons.");
1096 s->opt[i].type = SANE_TYPE_BOOL;
1097 s->opt[i].cap = SANE_CAP_SOFT_DETECT |
1098 SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
1099 if (i - OPT_BUTTON_0 >= dev->usbDev.Caps.bButtons )
1100 _DISABLE(i);
1101
1102 s->opt[i].unit = SANE_UNIT_NONE;
1103 s->opt[i].size = sizeof (SANE_Word);
1104 s->opt[i].constraint_type = SANE_CONSTRAINT_NONE;
1105 s->opt[i].constraint.range = 0;
1106 s->val[i].w = SANE_FALSE;
1107 }
1108
1109 usb_UpdateButtonStatus( s );
1110 return SANE_STATUS_GOOD;
1111 }
1112
1113 /** Function to retrieve the vendor and product id from a given string
1114 * @param src - string, that should be investigated
1115 * @param dest - pointer to a string to receive the USB ID
1116 */
1117 static void
decodeUsbIDs(char * src,char ** dest)1118 decodeUsbIDs( char *src, char **dest )
1119 {
1120 const char *name;
1121 char *tmp = *dest;
1122 int len = strlen(_SECTION);
1123
1124 if( isspace(src[len])) {
1125 strncpy( tmp, &src[len+1], (strlen(src)-(len+1)));
1126 tmp[(strlen(src)-(len+1))] = '\0';
1127 }
1128
1129 name = tmp;
1130 name = sanei_config_skip_whitespace( name );
1131
1132 if( '\0' == name[0] ) {
1133 DBG( _DBG_SANE_INIT, "next device uses autodetection\n" );
1134 } else {
1135
1136 u_short pi = 0, vi = 0;
1137
1138 if( *name ) {
1139
1140 name = sanei_config_get_string( name, &tmp );
1141 if( tmp ) {
1142 vi = strtol( tmp, 0, 0 );
1143 free( tmp );
1144 }
1145 }
1146
1147 name = sanei_config_skip_whitespace( name );
1148 if( *name ) {
1149
1150 name = sanei_config_get_string( name, &tmp );
1151 if( tmp ) {
1152 pi = strtol( tmp, 0, 0 );
1153 free( tmp );
1154 }
1155 }
1156
1157 /* create what we need to go through our device list...*/
1158 sprintf( *dest, "0x%04X-0x%04X", vi, pi );
1159 DBG( _DBG_SANE_INIT, "next device is a USB device (%s)\n", *dest );
1160 }
1161 }
1162
1163 #define _INT 0
1164 #define _FLOAT 1
1165
1166 /** function to decode an value and give it back to the caller.
1167 * @param src - pointer to the source string to check
1168 * @param opt - string that keeps the option name to check src for
1169 * @param what - _FLOAT or _INT
1170 * @param result - pointer to the var that should receive our result
1171 * @param def - default value that result should be in case of any error
1172 * @return The function returns SANE_TRUE if the option has been found,
1173 * if not, it returns SANE_FALSE
1174 */
1175 static SANE_Bool
decodeVal(char * src,char * opt,int what,void * result,void * def)1176 decodeVal( char *src, char *opt, int what, void *result, void *def )
1177 {
1178 char *tmp, *tmp2;
1179 const char *name;
1180
1181 /* skip the option string */
1182 name = (const char*)&src[strlen("option")];
1183
1184 /* get the name of the option */
1185 name = sanei_config_get_string( name, &tmp );
1186
1187 if( tmp ) {
1188
1189 /* on success, compare with the given one */
1190 if( 0 == strcmp( tmp, opt )) {
1191
1192 DBG( _DBG_SANE_INIT, "Decoding option >%s<\n", opt );
1193
1194 if( _INT == what ) {
1195
1196 /* assign the default value for this option... */
1197 *((int*)result) = *((int*)def);
1198
1199 if( *name ) {
1200
1201 /* get the configuration value and decode it */
1202 name = sanei_config_get_string( name, &tmp2 );
1203
1204 if( tmp2 ) {
1205 *((int*)result) = strtol( tmp2, 0, 0 );
1206 free( tmp2 );
1207 }
1208 }
1209 free( tmp );
1210 return SANE_TRUE;
1211
1212 } else if( _FLOAT == what ) {
1213
1214 /* assign the default value for this option... */
1215 *((double*)result) = *((double*)def);
1216
1217 if( *name ) {
1218
1219 /* get the configuration value and decode it */
1220 name = sanei_config_get_string( name, &tmp2 );
1221
1222 if( tmp2 ) {
1223 *((double*)result) = strtod( tmp2, 0 );
1224 free( tmp2 );
1225 }
1226 }
1227 free( tmp );
1228 return SANE_TRUE;
1229 }
1230 }
1231 free( tmp );
1232 }
1233 return SANE_FALSE;
1234 }
1235
1236 /** function to retrieve the device name of a given string
1237 * @param src - string that keeps the option name to check src for
1238 * @param dest - pointer to the string, that should receive the detected
1239 * devicename
1240 * @return The function returns SANE_TRUE if the devicename has been found,
1241 * if not, it returns SANE_FALSE
1242 */
1243 static SANE_Bool
decodeDevName(char * src,char * dest)1244 decodeDevName( char *src, char *dest )
1245 {
1246 char *tmp;
1247 const char *name;
1248
1249 if( 0 == strncmp( "device", src, 6 )) {
1250
1251 name = (const char*)&src[strlen("device")];
1252 name = sanei_config_skip_whitespace( name );
1253
1254 DBG( _DBG_SANE_INIT, "Decoding device name >%s<\n", name );
1255
1256 if( *name ) {
1257 name = sanei_config_get_string( name, &tmp );
1258 if( tmp ) {
1259
1260 strcpy( dest, tmp );
1261 free( tmp );
1262 return SANE_TRUE;
1263 }
1264 }
1265 }
1266 return SANE_FALSE;
1267 }
1268
1269 /** attach a device to the backend
1270 */
1271 static SANE_Status
attach(const char * dev_name,CnfDef * cnf,Plustek_Device ** devp)1272 attach( const char *dev_name, CnfDef *cnf, Plustek_Device **devp )
1273 {
1274 int cntr;
1275 int result;
1276 int handle;
1277 Plustek_Device *dev;
1278
1279 DBG( _DBG_SANE_INIT, "attach (%s, %p, %p)\n",
1280 dev_name, (void *)cnf, (void *)devp);
1281 /* already attached ?*/
1282 for( dev = first_dev; dev; dev = dev->next ) {
1283
1284 if( 0 == strcmp( dev->sane.name, dev_name )) {
1285 if( devp )
1286 *devp = dev;
1287
1288 return SANE_STATUS_GOOD;
1289 }
1290 }
1291
1292 /* allocate some memory for the device */
1293 dev = malloc( sizeof (*dev));
1294 if( NULL == dev )
1295 return SANE_STATUS_NO_MEM;
1296
1297 /* assign all the stuff we need for this device... */
1298
1299 memset(dev, 0, sizeof (*dev));
1300
1301 dev->fd = -1;
1302 dev->name = strdup(dev_name); /* hold it double to avoid */
1303 dev->sane.name = dev->name; /* compiler warnings */
1304 dev->sane.vendor = "Plustek";
1305 dev->initialized = -1; /* will be used as index too */
1306 dev->calFile = NULL;
1307 dev->transferRate = DEFAULT_RATE;
1308
1309 memcpy( &dev->adj, &cnf->adj, sizeof(AdjDef));
1310
1311 show_cnf( cnf );
1312
1313 strncpy( dev->usbId, cnf->usbId, _MAX_ID_LEN );
1314
1315 if( cnf->adj.lampOff >= 0 )
1316 dev->usbDev.dwLampOnPeriod = cnf->adj.lampOff;
1317
1318 if( cnf->adj.lampOffOnEnd >= 0 )
1319 dev->usbDev.bLampOffOnEnd = cnf->adj.lampOffOnEnd;
1320
1321 /* go ahead and open the scanner device */
1322 handle = usbDev_open( dev, usbDevs, SANE_FALSE );
1323 if( handle < 0 ) {
1324 DBG( _DBG_ERROR,"open failed: %d\n", handle );
1325 return SANE_STATUS_IO_ERROR;
1326 }
1327
1328 /* okay, so assign the handle and the scanner type */
1329 dev->fd = handle;
1330 if( usb_IsSheetFedDevice( dev ))
1331 dev->sane.type = SANE_I18N("sheetfed scanner");
1332 else
1333 dev->sane.type = SANE_I18N("flatbed scanner");
1334
1335 result = usbDev_getCaps( dev );
1336 if( result < 0 ) {
1337 DBG( _DBG_ERROR, "usbDev_getCaps() failed(%d)\n", result);
1338 usbDev_close(dev);
1339 return SANE_STATUS_IO_ERROR;
1340 }
1341
1342 /* save the info we got from the driver */
1343 DBG( _DBG_INFO, "Scanner information:\n" );
1344 if( NULL != dev->usbDev.ModelStr )
1345 dev->sane.model = dev->usbDev.ModelStr;
1346 else
1347 dev->sane.model = "USB-Device";
1348
1349 DBG( _DBG_INFO, "Vendor : %s\n", dev->sane.vendor );
1350 DBG( _DBG_INFO, "Model : %s\n", dev->sane.model );
1351 DBG( _DBG_INFO, "Flags : 0x%08lx\n", dev->caps.dwFlag );
1352
1353 dev->max_x = dev->caps.wMaxExtentX*MM_PER_INCH/_MEASURE_BASE;
1354 dev->max_y = dev->caps.wMaxExtentY*MM_PER_INCH/_MEASURE_BASE;
1355
1356 /* calculate the size of the resolution list +
1357 * one more to avoid a buffer overflow, then allocate it...
1358 */
1359 dev->res_list = (SANE_Int *)
1360 calloc((((dev->usbDev.Caps.OpticDpi.x*16)-_DEF_DPI)/25+1),
1361 sizeof (SANE_Int));
1362
1363 if (NULL == dev->res_list) {
1364 DBG( _DBG_ERROR, "calloc failed: %s\n", strerror(errno));
1365 usbDev_close(dev);
1366 return SANE_STATUS_INVAL;
1367 }
1368
1369 /* build up the resolution table */
1370 dev->res_list_size = 0;
1371 for(cntr = _DEF_DPI; cntr <= (dev->usbDev.Caps.OpticDpi.x*16); cntr += 25){
1372 dev->res_list_size++;
1373 dev->res_list[dev->res_list_size - 1] = (SANE_Int)cntr;
1374 }
1375
1376 /* set the limits */
1377 dev->dpi_range.min = _DEF_DPI;
1378 dev->dpi_range.max = dev->usbDev.Caps.OpticDpi.x * 2;
1379 dev->x_range.max = SANE_FIX(dev->max_x);
1380 dev->y_range.max = SANE_FIX(dev->max_y);
1381
1382 dev->fd = handle;
1383 drvclose( dev );
1384
1385 DBG( _DBG_SANE_INIT, "attach: model = >%s<\n", dev->sane.model );
1386
1387 ++num_devices;
1388 dev->next = first_dev;
1389 first_dev = dev;
1390
1391 if (devp)
1392 *devp = dev;
1393
1394 return SANE_STATUS_GOOD;
1395 }
1396
1397 /** function to preset a configuration structure
1398 * @param cnf - pointer to the structure that should be initialized
1399 */
1400 static void
init_config_struct(CnfDef * cnf)1401 init_config_struct( CnfDef *cnf )
1402 {
1403 memset(cnf, 0, sizeof(CnfDef));
1404
1405 cnf->adj.warmup = -1;
1406 cnf->adj.lampOff = -1;
1407 cnf->adj.lampOffOnEnd = -1;
1408
1409 cnf->adj.posShadingY = -1;
1410 cnf->adj.tpaShadingY = -1;
1411 cnf->adj.negShadingY = -1;
1412 cnf->adj.rgain = -1;
1413 cnf->adj.ggain = -1;
1414 cnf->adj.bgain = -1;
1415 cnf->adj.rofs = -1;
1416 cnf->adj.gofs = -1;
1417 cnf->adj.bofs = -1;
1418 cnf->adj.rlampoff = -1;
1419 cnf->adj.glampoff = -1;
1420 cnf->adj.blampoff = -1;
1421
1422 cnf->adj.incDarkTgt = 1;
1423
1424 cnf->adj.graygamma = 1.0;
1425 cnf->adj.rgamma = 1.0;
1426 cnf->adj.ggamma = 1.0;
1427 cnf->adj.bgamma = 1.0;
1428 }
1429
1430 /** initialize the backend
1431 */
1432 SANE_Status
sane_init(SANE_Int * version_code,SANE_Auth_Callback authorize)1433 sane_init( SANE_Int *version_code, SANE_Auth_Callback authorize )
1434 {
1435 char str[PATH_MAX] = _DEFAULT_DEVICE;
1436 CnfDef config;
1437 size_t len;
1438 FILE *fp;
1439
1440 DBG_INIT();
1441
1442 sanei_usb_init();
1443 sanei_lm983x_init();
1444 sanei_thread_init();
1445 sanei_access_init(STRINGIFY(BACKEND_NAME));
1446
1447 #if defined PACKAGE && defined VERSION
1448 DBG( _DBG_INFO, "Plustek backend V"BACKEND_VERSION", part of "
1449 PACKAGE " " VERSION "\n");
1450 #else
1451 DBG( _DBG_INFO, "Plustek backend V"BACKEND_VERSION"\n" );
1452 #endif
1453
1454 /* do some presettings... */
1455 auth = authorize;
1456 first_dev = NULL;
1457 first_handle = NULL;
1458 num_devices = 0;
1459 usbDevs = NULL;
1460
1461 /* initialize the configuration structure */
1462 init_config_struct( &config );
1463
1464 /* try and get a list of all connected AND supported devices */
1465 usbGetList( &usbDevs );
1466
1467 if( version_code != NULL )
1468 *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
1469
1470 fp = sanei_config_open( PLUSTEK_CONFIG_FILE );
1471
1472 /* default to _DEFAULT_DEVICE instead of insisting on config file */
1473 if( NULL == fp ) {
1474 return attach( _DEFAULT_DEVICE, &config, 0 );
1475 }
1476
1477 while( sanei_config_read( str, sizeof(str), fp)) {
1478
1479 DBG( _DBG_SANE_INIT, ">%s<\n", str );
1480 if( str[0] == '#') /* ignore line comments */
1481 continue;
1482
1483 len = strlen(str);
1484 if( 0 == len )
1485 continue; /* ignore empty lines */
1486
1487 /* check for options */
1488 if( 0 == strncmp(str, "option", 6)) {
1489
1490 int ival;
1491 double dval;
1492
1493 ival = -1;
1494 decodeVal( str, "warmup", _INT, &config.adj.warmup, &ival);
1495 decodeVal( str, "lampOff", _INT, &config.adj.lampOff, &ival);
1496 decodeVal( str, "lOffOnEnd", _INT, &config.adj.lampOffOnEnd,&ival);
1497 decodeVal( str, "posShadingY",_INT, &config.adj.posShadingY,&ival);
1498 decodeVal( str, "tpaShadingY",_INT, &config.adj.tpaShadingY,&ival);
1499 decodeVal( str, "negShadingY",_INT, &config.adj.negShadingY,&ival);
1500 decodeVal( str, "red_gain", _INT, &config.adj.rgain, &ival);
1501 decodeVal( str, "green_gain", _INT, &config.adj.ggain, &ival);
1502 decodeVal( str, "blue_gain", _INT, &config.adj.bgain, &ival);
1503 decodeVal( str, "red_offset", _INT, &config.adj.rofs, &ival);
1504 decodeVal( str, "green_offset" , _INT, &config.adj.gofs, &ival);
1505 decodeVal( str, "blue_offset", _INT, &config.adj.bofs, &ival);
1506 decodeVal( str, "red_lampoff", _INT, &config.adj.rlampoff,&ival);
1507 decodeVal( str, "green_lampoff", _INT, &config.adj.glampoff,&ival);
1508 decodeVal( str, "blue_lampoff", _INT, &config.adj.blampoff,&ival);
1509
1510 ival = 0;
1511 decodeVal( str, "enableTPA", _INT, &config.adj.enableTpa, &ival);
1512 decodeVal( str, "cacheCalData",
1513 _INT, &config.adj.cacheCalData,&ival);
1514 decodeVal( str, "altCalibration",
1515 _INT, &config.adj.altCalibrate,&ival);
1516 decodeVal( str, "skipCalibration",
1517 _INT, &config.adj.skipCalibration,&ival);
1518 decodeVal( str, "skipFine",
1519 _INT, &config.adj.skipFine,&ival);
1520 decodeVal( str, "skipFineWhite",
1521 _INT, &config.adj.skipFineWhite,&ival);
1522 decodeVal( str, "skipDarkStrip",
1523 _INT, &config.adj.skipDarkStrip,&ival);
1524 decodeVal( str, "incDarkTarget",
1525 _INT, &config.adj.incDarkTgt,&ival);
1526 decodeVal( str, "invertNegatives",
1527 _INT, &config.adj.invertNegatives,&ival);
1528 decodeVal( str, "disableSpeedup",
1529 _INT, &config.adj.disableSpeedup,&ival);
1530
1531 decodeVal( str, "posOffX", _INT, &config.adj.pos.x, &ival );
1532 decodeVal( str, "posOffY", _INT, &config.adj.pos.y, &ival );
1533
1534 decodeVal( str, "negOffX", _INT, &config.adj.neg.x, &ival );
1535 decodeVal( str, "negOffY", _INT, &config.adj.neg.y, &ival );
1536
1537 decodeVal( str, "tpaOffX", _INT, &config.adj.tpa.x, &ival );
1538 decodeVal( str, "tpaOffY", _INT, &config.adj.tpa.y, &ival );
1539
1540 decodeVal( str, "mov", _INT, &config.adj.mov, &ival);
1541
1542 dval = 1.0;
1543 decodeVal( str, "grayGamma", _FLOAT, &config.adj.graygamma,&dval);
1544 decodeVal( str, "redGamma", _FLOAT, &config.adj.rgamma, &dval );
1545 decodeVal( str, "greenGamma", _FLOAT, &config.adj.ggamma, &dval );
1546 decodeVal( str, "blueGamma", _FLOAT, &config.adj.bgamma, &dval );
1547 continue;
1548
1549 /* check for sections: */
1550 } else if( 0 == strncmp( str, _SECTION, strlen(_SECTION))) {
1551
1552 char *tmp;
1553
1554 /* new section, try and attach previous device */
1555 if( config.devName[0] != '\0' ) {
1556 attach( config.devName, &config, 0 );
1557 } else {
1558 if( first_dev != NULL ) {
1559 DBG( _DBG_WARNING, "section contains no device name,"
1560 " ignored!\n" );
1561 }
1562 }
1563
1564 /* re-initialize the configuration structure */
1565 init_config_struct( &config );
1566
1567 tmp = config.usbId;
1568 decodeUsbIDs( str, &tmp );
1569
1570 DBG( _DBG_SANE_INIT, "... next device\n" );
1571 continue;
1572
1573 } else if( SANE_TRUE == decodeDevName( str, config.devName )) {
1574 continue;
1575 }
1576
1577 /* ignore other stuff... */
1578 DBG( _DBG_SANE_INIT, "ignoring >%s<\n", str );
1579 }
1580 fclose (fp);
1581
1582 /* try to attach the last device in the config file... */
1583 if( config.devName[0] != '\0' )
1584 attach( config.devName, &config, 0 );
1585
1586 return SANE_STATUS_GOOD;
1587 }
1588
1589 /** cleanup the backend...
1590 */
1591 void
sane_exit(void)1592 sane_exit( void )
1593 {
1594 DevList *tmp;
1595 Plustek_Device *dev, *next;
1596
1597 DBG( _DBG_SANE_INIT, "sane_exit\n" );
1598
1599 for( dev = first_dev; dev; ) {
1600
1601 next = dev->next;
1602
1603 /* call the shutdown function of each device... */
1604 usbDev_shutdown( dev );
1605
1606 /* we're doin' this to avoid compiler warnings as dev->sane.name
1607 * is defined as const char*
1608 */
1609 if( dev->sane.name )
1610 free( dev->name );
1611
1612 if( dev->calFile )
1613 free( dev->calFile );
1614
1615 if( dev->res_list )
1616 free( dev->res_list );
1617 free( dev );
1618
1619 dev = next;
1620 }
1621
1622 if( devlist )
1623 free( devlist );
1624
1625 while( usbDevs ) {
1626 tmp = usbDevs->next;
1627 free( usbDevs );
1628 usbDevs = tmp;
1629 }
1630
1631 usbDevs = NULL;
1632 devlist = NULL;
1633 auth = NULL;
1634 first_dev = NULL;
1635 first_handle = NULL;
1636 }
1637
1638 /** return a list of all devices
1639 */
1640 SANE_Status
sane_get_devices(const SANE_Device *** device_list,SANE_Bool local_only)1641 sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only )
1642 {
1643 int i;
1644 Plustek_Device *dev;
1645
1646 DBG(_DBG_SANE_INIT, "sane_get_devices (%p, %ld)\n",
1647 (void *)device_list, (long) local_only);
1648
1649 /* already called, so cleanup */
1650 if( devlist )
1651 free( devlist );
1652
1653 devlist = malloc((num_devices + 1) * sizeof (devlist[0]));
1654 if ( NULL == devlist )
1655 return SANE_STATUS_NO_MEM;
1656
1657 i = 0;
1658 for (dev = first_dev; i < num_devices; dev = dev->next)
1659 devlist[i++] = &dev->sane;
1660 devlist[i++] = 0;
1661
1662 *device_list = devlist;
1663 return SANE_STATUS_GOOD;
1664 }
1665
1666 /** open the sane device
1667 */
1668 SANE_Status
sane_open(SANE_String_Const devicename,SANE_Handle * handle)1669 sane_open( SANE_String_Const devicename, SANE_Handle* handle )
1670 {
1671 SANE_Status status;
1672 Plustek_Device *dev;
1673 Plustek_Scanner *s;
1674 CnfDef config;
1675
1676 DBG( _DBG_SANE_INIT, "sane_open - %s\n", devicename );
1677
1678 if( devicename[0] ) {
1679 for( dev = first_dev; dev; dev = dev->next ) {
1680 if( strcmp( dev->sane.name, devicename ) == 0 )
1681 break;
1682 }
1683
1684 if( !dev ) {
1685
1686 memset(&config, 0, sizeof(CnfDef));
1687
1688 status = attach( devicename, &config, &dev );
1689 if( SANE_STATUS_GOOD != status )
1690 return status;
1691 }
1692 } else {
1693 /* empty devicename -> use first device */
1694 dev = first_dev;
1695 }
1696
1697 if( !dev )
1698 return SANE_STATUS_INVAL;
1699
1700 s = malloc (sizeof (*s));
1701 if( NULL == s )
1702 return SANE_STATUS_NO_MEM;
1703
1704 memset(s, 0, sizeof (*s));
1705 s->r_pipe = -1;
1706 s->w_pipe = -1;
1707 s->hw = dev;
1708 s->scanning = SANE_FALSE;
1709 s->calibrating = SANE_FALSE;
1710
1711 init_options(s);
1712
1713 /* insert newly opened handle into list of open handles: */
1714 s->next = first_handle;
1715 first_handle = s;
1716 *handle = s;
1717
1718 return SANE_STATUS_GOOD;
1719 }
1720
1721 /**
1722 */
1723 void
sane_close(SANE_Handle handle)1724 sane_close( SANE_Handle handle )
1725 {
1726 Plustek_Scanner *prev, *s = handle;
1727
1728 DBG( _DBG_SANE_INIT, "sane_close\n" );
1729
1730 if( s->calibrating )
1731 do_cancel( s, SANE_FALSE );
1732
1733 /* remove handle from list of open handles: */
1734 prev = 0;
1735
1736 for( s = first_handle; s; s = s->next ) {
1737 if( s == handle )
1738 break;
1739 prev = s;
1740 }
1741
1742 if (!s) {
1743 DBG( _DBG_ERROR, "close: invalid handle %p\n", handle);
1744 return;
1745 }
1746
1747 close_pipe( s );
1748
1749 if( NULL != s->buf )
1750 free(s->buf);
1751
1752 drvclose( s->hw );
1753
1754 if (prev)
1755 prev->next = s->next;
1756 else
1757 first_handle = s->next;
1758
1759 free(s);
1760 }
1761
1762 /** goes through a string list and returns the start-address of the string
1763 * that has been found, or NULL on error
1764 */
1765 static const SANE_String_Const*
search_string_list(const SANE_String_Const * list,SANE_String value)1766 search_string_list( const SANE_String_Const *list, SANE_String value )
1767 {
1768 while( *list != NULL && strcmp(value, *list) != 0 )
1769 ++list;
1770
1771 if( *list == NULL )
1772 return NULL;
1773
1774 return list;
1775 }
1776
1777 /**
1778 */
1779 static int
do_calibration(void * args)1780 do_calibration( void *args )
1781 {
1782 Plustek_Scanner *s = (Plustek_Scanner *)args;
1783 Plustek_Device *dev = s->hw;
1784 DCapsDef *caps = &dev->usbDev.Caps;
1785 int scanmode, rc;
1786 int modes[] = { COLOR_BW, COLOR_256GRAY, COLOR_GRAY16,
1787 COLOR_TRUE24, COLOR_TRUE48 };
1788
1789 thread_entry();
1790
1791 /* if the device does only support color scanning, there's no need
1792 * to calibrate the gray modes
1793 */
1794 if (caps->workaroundFlag & _WAF_GRAY_FROM_COLOR)
1795 scanmode = 3;
1796 else
1797 scanmode = 0;
1798
1799 for ( ; scanmode < 5; scanmode++ ) {
1800
1801 if (caps->workaroundFlag & _WAF_ONLY_8BIT) {
1802
1803 if ((modes[scanmode] == COLOR_GRAY16) ||
1804 (modes[scanmode] == COLOR_TRUE48)) {
1805 continue;
1806 }
1807 }
1808
1809 dev->scanning.dwFlag |= SCANFLAG_Calibration;
1810
1811 if (SANE_STATUS_GOOD == local_sane_start(s, modes[scanmode])) {
1812
1813 /* prepare for scanning: speed-test, warmup, calibration */
1814 rc = usbDev_Prepare( dev, s->buf );
1815 if( rc != 0 || scanmode == 4) {
1816 if (rc != 0 )
1817 DBG(_DBG_INFO,"Calibration canceled!\n");
1818 m_fStart = SANE_TRUE;
1819 m_fAutoPark = SANE_TRUE;
1820 }
1821
1822 drvclose( dev );
1823 if( rc != 0 )
1824 break;
1825 } else {
1826 DBG(_DBG_ERROR, "local_sane_start() failed!\n");
1827 break;
1828 }
1829 }
1830
1831 /* restore the settings */
1832 dev->scanning.dwFlag &= ~SCANFLAG_Calibration;
1833 s->calibrating = SANE_FALSE;
1834 return 0;
1835 }
1836
1837 /** return or set the parameter values, also do some checks
1838 */
1839 SANE_Status
sane_control_option(SANE_Handle handle,SANE_Int option,SANE_Action action,void * value,SANE_Int * info)1840 sane_control_option( SANE_Handle handle, SANE_Int option,
1841 SANE_Action action, void *value, SANE_Int *info )
1842 {
1843 Plustek_Scanner *s = (Plustek_Scanner *)handle;
1844 Plustek_Device *dev = s->hw;
1845 AdjDef *adj = &dev->adj;
1846 DCapsDef *caps = &dev->usbDev.Caps;
1847 SANE_Status status;
1848 const SANE_String_Const *optval;
1849 int scanmode;
1850
1851 if (s->scanning)
1852 return SANE_STATUS_DEVICE_BUSY;
1853
1854 /* in calibration mode, we do not allow setting any value! */
1855 if(s->calibrating) {
1856 if (action == SANE_ACTION_SET_VALUE) {
1857 if (option == OPT_CALIBRATE) {
1858 if( NULL != info )
1859 *info |= SANE_INFO_RELOAD_OPTIONS;
1860 do_cancel(s, SANE_TRUE);
1861 return SANE_STATUS_GOOD;
1862 }
1863
1864 /* okay, we need some exceptions */
1865 switch (option) {
1866 case OPT_TL_X:
1867 case OPT_TL_Y:
1868 case OPT_BR_X:
1869 case OPT_BR_Y: break;
1870 default: return SANE_STATUS_DEVICE_BUSY;
1871 }
1872 }
1873 }
1874
1875 if ((option < 0) || (option >= NUM_OPTIONS))
1876 return SANE_STATUS_INVAL;
1877
1878 if (NULL != info)
1879 *info = 0;
1880
1881 switch( action ) {
1882
1883 case SANE_ACTION_GET_VALUE:
1884
1885 switch (option) {
1886 case OPT_PREVIEW:
1887 case OPT_NUM_OPTS:
1888 case OPT_RESOLUTION:
1889 case OPT_BIT_DEPTH:
1890 case OPT_TL_X:
1891 case OPT_TL_Y:
1892 case OPT_BR_X:
1893 case OPT_BR_Y:
1894 case OPT_LAMPSWITCH:
1895 case OPT_CUSTOM_GAMMA:
1896 case OPT_LAMPOFF_ONEND:
1897 case OPT_LOFF4DARK:
1898 case OPT_CACHECAL:
1899 case OPT_SPEEDUP:
1900 case OPT_OVR_REDGAIN:
1901 case OPT_OVR_GREENGAIN:
1902 case OPT_OVR_BLUEGAIN:
1903 case OPT_OVR_REDOFS:
1904 case OPT_OVR_GREENOFS:
1905 case OPT_OVR_BLUEOFS:
1906 case OPT_OVR_RED_LOFF:
1907 case OPT_OVR_GREEN_LOFF:
1908 case OPT_OVR_BLUE_LOFF:
1909 case OPT_LAMPOFF_TIMER:
1910 case OPT_WARMUPTIME:
1911 *(SANE_Word *)value = s->val[option].w;
1912 break;
1913
1914 case OPT_BUTTON_0:
1915 if(!s->calibrating)
1916 usb_UpdateButtonStatus(s);
1917 // fall through
1918 case OPT_BUTTON_1:
1919 case OPT_BUTTON_2:
1920 case OPT_BUTTON_3:
1921 case OPT_BUTTON_4:
1922 /* copy the button state */
1923 *(SANE_Word*)value = s->val[option].w;
1924 /* clear the button state */
1925 s->val[option].w = SANE_FALSE;
1926 break;
1927
1928 case OPT_CONTRAST:
1929 case OPT_BRIGHTNESS:
1930 *(SANE_Word *)value =
1931 (s->val[option].w << SANE_FIXED_SCALE_SHIFT);
1932 break;
1933
1934 case OPT_MODE:
1935 case OPT_EXT_MODE:
1936 strcpy ((char *) value,
1937 s->opt[option].constraint.string_list[s->val[option].w]);
1938 break;
1939
1940 /* word array options: */
1941 case OPT_GAMMA_VECTOR:
1942 DBG( _DBG_INFO, "Reading MASTER gamma.\n" );
1943 memcpy( value, s->val[option].wa, s->opt[option].size );
1944 break;
1945
1946 case OPT_GAMMA_VECTOR_R:
1947 DBG( _DBG_INFO, "Reading RED gamma.\n" );
1948 memcpy( value, s->val[option].wa, s->opt[option].size );
1949 break;
1950
1951 case OPT_GAMMA_VECTOR_G:
1952 DBG( _DBG_INFO, "Reading GREEN gamma.\n" );
1953 memcpy( value, s->val[option].wa, s->opt[option].size );
1954 break;
1955
1956 case OPT_GAMMA_VECTOR_B:
1957 DBG( _DBG_INFO, "Reading BLUE gamma.\n" );
1958 memcpy( value, s->val[option].wa, s->opt[option].size );
1959 break;
1960 default:
1961 return SANE_STATUS_INVAL;
1962 }
1963 break;
1964
1965 case SANE_ACTION_SET_VALUE:
1966 status = sanei_constrain_value( s->opt + option, value, info );
1967 if( SANE_STATUS_GOOD != status )
1968 return status;
1969
1970 optval = NULL;
1971 if( SANE_CONSTRAINT_STRING_LIST == s->opt[option].constraint_type ) {
1972
1973 optval = search_string_list( s->opt[option].constraint.string_list,
1974 (char *) value);
1975 if( NULL == optval )
1976 return SANE_STATUS_INVAL;
1977 }
1978
1979 switch (option) {
1980
1981 case OPT_RESOLUTION: {
1982 int n;
1983 int min_d = dev->res_list[dev->res_list_size - 1];
1984 int v = *(SANE_Word *)value;
1985 int best = v;
1986
1987 for( n = 0; n < dev->res_list_size; n++ ) {
1988 int d = abs(v - dev->res_list[n]);
1989
1990 if( d < min_d ) {
1991 min_d = d;
1992 best = dev->res_list[n];
1993 }
1994 }
1995
1996 s->val[option].w = (SANE_Word)best;
1997
1998 if( v != best )
1999 *(SANE_Word *)value = best;
2000
2001 if( NULL != info ) {
2002 if( v != best )
2003 *info |= SANE_INFO_INEXACT;
2004 *info |= SANE_INFO_RELOAD_PARAMS;
2005 }
2006 break;
2007 }
2008
2009 case OPT_PREVIEW:
2010 case OPT_BIT_DEPTH:
2011 case OPT_TL_X:
2012 case OPT_TL_Y:
2013 case OPT_BR_X:
2014 case OPT_BR_Y:
2015 s->val[option].w = *(SANE_Word *)value;
2016 if( NULL != info )
2017 *info |= SANE_INFO_RELOAD_PARAMS;
2018 break;
2019
2020 case OPT_CACHECAL:
2021 s->val[option].w = *(SANE_Word *)value;
2022 dev->adj.cacheCalData = s->val[option].w;
2023 if( !dev->adj.cacheCalData )
2024 _DISABLE(OPT_CALIBRATE);
2025 else {
2026 if( usb_IsCISDevice(dev) || dev->adj.altCalibrate)
2027 _ENABLE(OPT_CALIBRATE);
2028 }
2029 if( NULL != info )
2030 *info |= SANE_INFO_RELOAD_OPTIONS;
2031 break;
2032
2033 case OPT_CALIBRATE:
2034 if (s->calibrating) {
2035 do_cancel( s, SANE_FALSE );
2036 s->calibrating = SANE_FALSE;
2037 } else {
2038 sc = s;
2039 s->r_pipe = -1;
2040 s->w_pipe = -1;
2041 s->reader_pid = sanei_thread_begin(do_calibration, s);
2042 s->calibrating = SANE_TRUE;
2043 signal( SIGCHLD, sig_chldhandler );
2044 }
2045 if (NULL != info)
2046 *info |= SANE_INFO_RELOAD_OPTIONS;
2047 break;
2048
2049 case OPT_SPEEDUP:
2050 s->val[option].w = *(SANE_Word *)value;
2051 dev->adj.disableSpeedup = !(s->val[option].w);
2052 break;
2053
2054 case OPT_LOFF4DARK:
2055 s->val[option].w = *(SANE_Word *)value;
2056 dev->adj.skipDarkStrip = !(s->val[option].w);
2057 break;
2058
2059 case OPT_LAMPSWITCH:
2060 s->val[option].w = *(SANE_Word *)value;
2061 usb_LampSwitch( dev, s->val[option].w );
2062 if( s->val[option].w == 0 )
2063 usb_StopLampTimer( dev );
2064 else
2065 usb_StartLampTimer( dev );
2066 break;
2067
2068 case OPT_LAMPOFF_ONEND:
2069 s->val[option].w = *(SANE_Word *)value;
2070 dev->adj.lampOffOnEnd = s->val[option].w;
2071 usb_CheckAndCopyAdjs( dev );
2072 break;
2073
2074 case OPT_CUSTOM_GAMMA:
2075 s->val[option].w = *(SANE_Word *)value;
2076 if( NULL != info )
2077 *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
2078
2079 scanmode = getScanMode( s );
2080
2081 _DISABLE(OPT_GAMMA_VECTOR);
2082 _DISABLE(OPT_GAMMA_VECTOR_R);
2083 _DISABLE(OPT_GAMMA_VECTOR_G);
2084 _DISABLE(OPT_GAMMA_VECTOR_B);
2085
2086 if( SANE_TRUE == s->val[option].w ) {
2087 DBG( _DBG_INFO, "Using custom gamma settings.\n" );
2088 if((scanmode == COLOR_256GRAY) ||
2089 (scanmode == COLOR_GRAY16)) {
2090 _ENABLE(OPT_GAMMA_VECTOR);
2091 } else {
2092 _ENABLE(OPT_GAMMA_VECTOR_R);
2093 _ENABLE(OPT_GAMMA_VECTOR_G);
2094 _ENABLE(OPT_GAMMA_VECTOR_B);
2095 }
2096 } else {
2097
2098 DBG( _DBG_INFO, "NOT using custom gamma settings.\n" );
2099 initGammaSettings( s );
2100
2101 if((scanmode == COLOR_256GRAY) ||
2102 (scanmode == COLOR_GRAY16)) {
2103 _DISABLE(OPT_GAMMA_VECTOR);
2104 } else {
2105 _DISABLE(OPT_GAMMA_VECTOR_R);
2106 _DISABLE(OPT_GAMMA_VECTOR_G);
2107 _DISABLE(OPT_GAMMA_VECTOR_B);
2108 }
2109 }
2110 break;
2111
2112 case OPT_LAMPOFF_TIMER:
2113 s->val[option].w = (*(SANE_Word *)value);
2114 adj->lampOff = (*(SANE_Word *)value);
2115 usb_CheckAndCopyAdjs( dev );
2116 break;
2117
2118 case OPT_WARMUPTIME:
2119 s->val[option].w = (*(SANE_Word *)value);
2120 adj->warmup = (*(SANE_Word *)value);
2121 usb_CheckAndCopyAdjs( dev );
2122 break;
2123
2124 case OPT_OVR_REDGAIN:
2125 s->val[option].w = (*(SANE_Word *)value);
2126 adj->rgain = (*(SANE_Word *)value);
2127 break;
2128 case OPT_OVR_GREENGAIN:
2129 s->val[option].w = (*(SANE_Word *)value);
2130 adj->ggain = (*(SANE_Word *)value);
2131 break;
2132 case OPT_OVR_BLUEGAIN:
2133 s->val[option].w = (*(SANE_Word *)value);
2134 adj->bgain = (*(SANE_Word *)value);
2135 break;
2136 case OPT_OVR_REDOFS:
2137 s->val[option].w = (*(SANE_Word *)value);
2138 adj->rofs = (*(SANE_Word *)value);
2139 break;
2140 case OPT_OVR_GREENOFS:
2141 s->val[option].w = (*(SANE_Word *)value);
2142 adj->gofs = (*(SANE_Word *)value);
2143 break;
2144 case OPT_OVR_BLUEOFS:
2145 s->val[option].w = (*(SANE_Word *)value);
2146 adj->bofs = (*(SANE_Word *)value);
2147 break;
2148 case OPT_OVR_RED_LOFF:
2149 s->val[option].w = (*(SANE_Word *)value);
2150 adj->rlampoff = (*(SANE_Word *)value);
2151 break;
2152 case OPT_OVR_GREEN_LOFF:
2153 s->val[option].w = (*(SANE_Word *)value);
2154 adj->glampoff = (*(SANE_Word *)value);
2155 break;
2156 case OPT_OVR_BLUE_LOFF:
2157 s->val[option].w = (*(SANE_Word *)value);
2158 adj->blampoff = (*(SANE_Word *)value);
2159 break;
2160
2161 case OPT_CONTRAST:
2162 case OPT_BRIGHTNESS:
2163 s->val[option].w =
2164 ((*(SANE_Word *)value) >> SANE_FIXED_SCALE_SHIFT);
2165 break;
2166
2167 case OPT_MODE:
2168 s->val[option].w = optval - s->opt[option].constraint.string_list;
2169 scanmode = getScanMode( s );
2170
2171 _ENABLE(OPT_CONTRAST);
2172 _ENABLE(OPT_BIT_DEPTH);
2173 _ENABLE(OPT_CUSTOM_GAMMA);
2174 if (scanmode == COLOR_BW) {
2175 _DISABLE(OPT_CONTRAST);
2176 _DISABLE(OPT_CUSTOM_GAMMA);
2177 _DISABLE(OPT_BIT_DEPTH);
2178 }
2179
2180 if (caps->workaroundFlag & _WAF_ONLY_8BIT)
2181 _DISABLE(OPT_BIT_DEPTH);
2182
2183 _DISABLE(OPT_GAMMA_VECTOR);
2184 _DISABLE(OPT_GAMMA_VECTOR_R);
2185 _DISABLE(OPT_GAMMA_VECTOR_G);
2186 _DISABLE(OPT_GAMMA_VECTOR_B);
2187
2188 if( s->val[OPT_CUSTOM_GAMMA].w &&
2189 !(s->opt[OPT_CUSTOM_GAMMA].cap & SANE_CAP_INACTIVE)) {
2190
2191 if((scanmode == COLOR_256GRAY) ||
2192 (scanmode == COLOR_GRAY16)) {
2193 _ENABLE(OPT_GAMMA_VECTOR);
2194 } else {
2195 _ENABLE(OPT_GAMMA_VECTOR_R);
2196 _ENABLE(OPT_GAMMA_VECTOR_G);
2197 _ENABLE(OPT_GAMMA_VECTOR_B);
2198 }
2199 }
2200 if( NULL != info )
2201 *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
2202 break;
2203
2204 case OPT_EXT_MODE: {
2205 s->val[option].w = optval - s->opt[option].constraint.string_list;
2206
2207 /* change the area and mode_list when changing the source
2208 */
2209 if( s->val[option].w == 0 ) {
2210 dev->scanning.sParam.bSource = SOURCE_Reflection;
2211
2212 dev->dpi_range.min = _DEF_DPI;
2213
2214 dev->x_range.max = SANE_FIX(dev->max_x);
2215 dev->y_range.max = SANE_FIX(dev->max_y);
2216 s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TLX);
2217 s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TLY);
2218 s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_BRX);
2219 s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_BRY);
2220
2221 s->opt[OPT_MODE].constraint.string_list = mode_list;
2222 s->val[OPT_MODE].w = 2; /* HEINER COLOR_TRUE24;*/
2223
2224 } else {
2225
2226 dev->dpi_range.min = _TPAMinDpi;
2227
2228 if( s->val[option].w == 1 ) {
2229
2230 dev->scanning.sParam.bSource = SOURCE_Transparency;
2231 if( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_LargeTPA ) {
2232 dev->x_range.max = SANE_FIX(_SCALE(_TPALargePageWidth));
2233 dev->y_range.max = SANE_FIX(_SCALE(_TPALargePageHeight));
2234 } else {
2235 dev->x_range.max = SANE_FIX(_SCALE(_TPAPageWidth));
2236 dev->y_range.max = SANE_FIX(_SCALE(_TPAPageHeight));
2237 }
2238 s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_TP_TLX);
2239 s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_TP_TLY);
2240 s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_TP_BRX);
2241 s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_TP_BRY);
2242
2243 } else {
2244 dev->scanning.sParam.bSource = SOURCE_Negative;
2245 if( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_LargeTPA ) {
2246 dev->x_range.max = SANE_FIX(_SCALE(_NegLargePageWidth));
2247 dev->y_range.max = SANE_FIX(_SCALE(_NegLargePageHeight));
2248 } else {
2249 dev->x_range.max = SANE_FIX(_SCALE(_NegPageWidth));
2250 dev->y_range.max = SANE_FIX(_SCALE(_NegPageHeight));
2251 }
2252 s->val[OPT_TL_X].w = SANE_FIX(_DEFAULT_NEG_TLX);
2253 s->val[OPT_TL_Y].w = SANE_FIX(_DEFAULT_NEG_TLY);
2254 s->val[OPT_BR_X].w = SANE_FIX(_DEFAULT_NEG_BRX);
2255 s->val[OPT_BR_Y].w = SANE_FIX(_DEFAULT_NEG_BRY);
2256 }
2257 s->opt[OPT_MODE].constraint.string_list = &mode_list[2];
2258 s->val[OPT_MODE].w = 0; /* COLOR_24 is the default */
2259 }
2260 if( s->val[OPT_LAMPSWITCH].w != 0 ) {
2261 usb_LampSwitch( dev, s->val[OPT_LAMPSWITCH].w );
2262 if( s->val[OPT_LAMPSWITCH].w == 0 )
2263 usb_StopLampTimer( dev );
2264 else
2265 usb_StartLampTimer( dev );
2266 }
2267
2268 _ENABLE(OPT_CONTRAST);
2269 if( NULL != info )
2270 *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
2271 break;
2272 }
2273 case OPT_GAMMA_VECTOR:
2274 DBG( _DBG_INFO, "Setting MASTER gamma.\n" );
2275 memcpy( s->val[option].wa, value, s->opt[option].size );
2276 checkGammaSettings(s);
2277 if( NULL != info )
2278 *info |= SANE_INFO_RELOAD_PARAMS;
2279 break;
2280
2281 case OPT_GAMMA_VECTOR_R:
2282 DBG( _DBG_INFO, "Setting RED gamma.\n" );
2283 memcpy( s->val[option].wa, value, s->opt[option].size );
2284 checkGammaSettings(s);
2285 if( NULL != info )
2286 *info |= SANE_INFO_RELOAD_PARAMS;
2287 break;
2288
2289 case OPT_GAMMA_VECTOR_G:
2290 DBG( _DBG_INFO, "Setting GREEN gamma.\n" );
2291 memcpy( s->val[option].wa, value, s->opt[option].size );
2292 checkGammaSettings(s);
2293 if( NULL != info )
2294 *info |= SANE_INFO_RELOAD_PARAMS;
2295 break;
2296
2297 case OPT_GAMMA_VECTOR_B:
2298 DBG( _DBG_INFO, "Setting BLUE gamma.\n" );
2299 memcpy( s->val[option].wa, value, s->opt[option].size );
2300 checkGammaSettings(s);
2301 if( NULL != info )
2302 *info |= SANE_INFO_RELOAD_PARAMS;
2303 break;
2304 default:
2305 return SANE_STATUS_INVAL;
2306 }
2307 break;
2308
2309 default:
2310 return SANE_STATUS_INVAL;
2311 }
2312
2313 return SANE_STATUS_GOOD;
2314 }
2315
2316 /** according to the option number, return a pointer to a descriptor
2317 */
2318 const SANE_Option_Descriptor*
sane_get_option_descriptor(SANE_Handle handle,SANE_Int option)2319 sane_get_option_descriptor( SANE_Handle handle, SANE_Int option )
2320 {
2321 Plustek_Scanner *s = (Plustek_Scanner *)handle;
2322
2323 if((option < 0) || (option >= NUM_OPTIONS))
2324 return NULL;
2325
2326 return &(s->opt[option]);
2327 }
2328
2329 /** return the current parameter settings
2330 */
2331 SANE_Status
sane_get_parameters(SANE_Handle handle,SANE_Parameters * params)2332 sane_get_parameters( SANE_Handle handle, SANE_Parameters *params )
2333 {
2334 int ndpi;
2335 int scanmode;
2336 Plustek_Scanner *s = (Plustek_Scanner *)handle;
2337
2338 /* if we're calling from within, calc best guess
2339 * do the same, if sane_get_parameters() is called
2340 * by a frontend before sane_start() is called
2341 */
2342 if((NULL == params) || (s->scanning != SANE_TRUE)) {
2343
2344 memset(&s->params, 0, sizeof (SANE_Parameters));
2345
2346 ndpi = s->val[OPT_RESOLUTION].w;
2347
2348 s->params.pixels_per_line = SANE_UNFIX(s->val[OPT_BR_X].w -
2349 s->val[OPT_TL_X].w) / MM_PER_INCH * ndpi;
2350
2351 s->params.lines = SANE_UNFIX( s->val[OPT_BR_Y].w -
2352 s->val[OPT_TL_Y].w) / MM_PER_INCH * ndpi;
2353
2354 /* pixels_per_line seems to be 8 * n. */
2355 /* s->params.pixels_per_line = s->params.pixels_per_line & ~7; debug only */
2356
2357 s->params.last_frame = SANE_TRUE;
2358 scanmode = getScanMode( s );
2359
2360 if( scanmode == COLOR_TRUE24 || scanmode == COLOR_TRUE48 ) {
2361 s->params.format = SANE_FRAME_RGB;
2362 s->params.bytes_per_line = 3 * s->params.pixels_per_line;
2363 } else {
2364 s->params.format = SANE_FRAME_GRAY;
2365 if (s->params.depth == 1)
2366 s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
2367 else
2368 s->params.bytes_per_line = s->params.pixels_per_line *
2369 s->params.depth / 8;
2370 }
2371
2372 /* if sane_get_parameters() was called before sane_start() */
2373 /* pass new values to the caller */
2374 if ((NULL != params) && (s->scanning != SANE_TRUE))
2375 *params = s->params;
2376 } else {
2377 *params = s->params;
2378 }
2379 return SANE_STATUS_GOOD;
2380 }
2381
2382 /** initiate the scan process
2383 */
2384 static SANE_Status
local_sane_start(Plustek_Scanner * s,int scanmode)2385 local_sane_start(Plustek_Scanner *s, int scanmode )
2386 {
2387 Plustek_Device *dev;
2388
2389 int result;
2390 int ndpi;
2391 int left, top;
2392 int width, height;
2393 double dpi_x, dpi_y;
2394 CropInfo crop;
2395 ScanInfo sinfo;
2396 SANE_Word tmp;
2397
2398 /* clear it out just in case */
2399 memset(&crop, 0, sizeof(crop));
2400
2401 dev = s->hw;
2402
2403 /* check if we're called from the option dialog! */
2404 if (usb_InCalibrationMode(dev))
2405 crop.ImgDef.dwFlag = SCANFLAG_Calibration;
2406
2407 /* open the driver and get some information about the scanner
2408 */
2409 dev->fd = usbDev_open( dev, NULL, SANE_TRUE );
2410 if( dev->fd < 0 ) {
2411 DBG( _DBG_ERROR, "sane_start: open failed: %d\n", errno);
2412
2413 if( errno == EBUSY )
2414 return SANE_STATUS_DEVICE_BUSY;
2415
2416 return SANE_STATUS_IO_ERROR;
2417 }
2418
2419 result = usbDev_getCaps( dev );
2420 if( result < 0 ) {
2421 DBG( _DBG_ERROR, "usbDev_getCaps() failed(%d)\n", result);
2422 sanei_access_unlock( dev->sane.name );
2423 usbDev_close( dev );
2424 return SANE_STATUS_IO_ERROR;
2425 }
2426
2427 /* All ready to go. Set image def and see what the scanner
2428 * says for crop info.
2429 */
2430 ndpi = s->val[OPT_RESOLUTION].w;
2431
2432 /* exchange the values as we can't deal with
2433 * negative heights and so on...*/
2434 tmp = s->val[OPT_TL_X].w;
2435 if( tmp > s->val[OPT_BR_X].w ) {
2436 DBG( _DBG_INFO, "exchanging BR-X - TL-X\n" );
2437 s->val[OPT_TL_X].w = s->val[OPT_BR_X].w;
2438 s->val[OPT_BR_X].w = tmp;
2439 }
2440
2441 tmp = s->val[OPT_TL_Y].w;
2442 if( tmp > s->val[OPT_BR_Y].w ) {
2443 DBG( _DBG_INFO, "exchanging BR-Y - TL-Y\n" );
2444 s->val[OPT_TL_Y].w = s->val[OPT_BR_Y].w;
2445 s->val[OPT_BR_Y].w = tmp;
2446 }
2447
2448 /* position and extent are always relative to 300 dpi */
2449 dpi_x = (double)dev->usbDev.Caps.OpticDpi.x;
2450 dpi_y = (double)dev->usbDev.Caps.OpticDpi.x * 2;
2451
2452 left = (int)(SANE_UNFIX (s->val[OPT_TL_X].w)*dpi_x/
2453 (MM_PER_INCH*(dpi_x/300.0)));
2454 top = (int)(SANE_UNFIX (s->val[OPT_TL_Y].w)*dpi_y/
2455 (MM_PER_INCH*(dpi_y/300.0)));
2456 width = (int)(SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) *
2457 dpi_x / (MM_PER_INCH *(dpi_x/300.0)));
2458 height = (int)(SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) *
2459 dpi_y / (MM_PER_INCH *(dpi_y/300.0)));
2460
2461 /* adjust mode list according to the model we use and the
2462 * source we have
2463 */
2464 DBG( _DBG_INFO, "scanmode = %u\n", scanmode );
2465
2466 crop.ImgDef.xyDpi.x = ndpi;
2467 crop.ImgDef.xyDpi.y = ndpi;
2468 crop.ImgDef.crArea.x = left; /* offset from left edge to area you want to scan */
2469 crop.ImgDef.crArea.y = top; /* offset from top edge to area you want to scan */
2470 crop.ImgDef.crArea.cx = width; /* always relative to 300 dpi */
2471 crop.ImgDef.crArea.cy = height;
2472 crop.ImgDef.wDataType = scanmode;
2473 crop.ImgDef.dwFlag |= SCANDEF_QualityScan;
2474
2475 switch( s->val[OPT_EXT_MODE].w ) {
2476 case 1: crop.ImgDef.dwFlag |= SCANDEF_Transparency; break;
2477 case 2: crop.ImgDef.dwFlag |= SCANDEF_Negative; break;
2478 default: break;
2479 }
2480
2481 result = usbDev_getCropInfo( dev, &crop );
2482 if( result < 0 ) {
2483 DBG( _DBG_ERROR, "usbDev_getCropInfo() failed(%d)\n", result );
2484 usbDev_close( dev );
2485 sanei_access_unlock( dev->sane.name );
2486 return SANE_STATUS_IO_ERROR;
2487 }
2488
2489 /* DataInf.dwAppPixelsPerLine = crop.dwPixelsPerLine; */
2490 s->params.pixels_per_line = crop.dwPixelsPerLine;
2491 s->params.bytes_per_line = crop.dwBytesPerLine;
2492 s->params.lines = crop.dwLinesPerArea;
2493
2494 /* build a SCANINFO block and get ready to scan it */
2495 crop.ImgDef.dwFlag |= SCANDEF_QualityScan;
2496
2497 /* remove that for preview scans */
2498 if( s->val[OPT_PREVIEW].w )
2499 crop.ImgDef.dwFlag &= (~SCANDEF_QualityScan);
2500
2501 /* set adjustments for brightness and contrast */
2502 sinfo.siBrightness = s->val[OPT_BRIGHTNESS].w;
2503 sinfo.siContrast = s->val[OPT_CONTRAST].w;
2504
2505 memcpy( &sinfo.ImgDef, &crop.ImgDef, sizeof(ImgDef));
2506
2507 DBG( _DBG_SANE_INIT, "brightness %i, contrast %i\n",
2508 sinfo.siBrightness, sinfo.siContrast );
2509
2510 result = usbDev_setScanEnv( dev, &sinfo );
2511 if( result < 0 ) {
2512 DBG( _DBG_ERROR, "usbDev_setScanEnv() failed(%d)\n", result );
2513 usbDev_close( dev );
2514 sanei_access_unlock( dev->sane.name );
2515 return SANE_STATUS_IO_ERROR;
2516 }
2517
2518 /* download gamma correction tables... */
2519 if( scanmode <= COLOR_GRAY16 ) {
2520 usbDev_setMap( dev, s->gamma_table[0], s->gamma_length, _MAP_MASTER);
2521 } else {
2522 usbDev_setMap( dev, s->gamma_table[1], s->gamma_length, _MAP_RED );
2523 usbDev_setMap( dev, s->gamma_table[2], s->gamma_length, _MAP_GREEN );
2524 usbDev_setMap( dev, s->gamma_table[3], s->gamma_length, _MAP_BLUE );
2525 }
2526
2527 tsecs = 0; /* reset timer */
2528
2529 result = usbDev_startScan( dev );
2530 if( result < 0 ) {
2531 DBG( _DBG_ERROR, "usbDev_startScan() failed(%d)\n", result );
2532 usbDev_close( dev );
2533 sanei_access_unlock( dev->sane.name );
2534 return SANE_STATUS_IO_ERROR;
2535 }
2536
2537 DBG( _DBG_SANE_INIT, "dwflag = 0x%lx dwBytesLine = %ld\n",
2538 dev->scanning.dwFlag, dev->scanning.dwBytesLine );
2539 DBG( _DBG_SANE_INIT, "Lines = %d\n", s->params.lines);
2540 DBG( _DBG_SANE_INIT, "Bytes per Line = %d\n", s->params.bytes_per_line );
2541 DBG( _DBG_SANE_INIT, "Bitdepth = %d\n", s->params.depth );
2542
2543 if (usb_InCalibrationMode(dev)) {
2544 if (s->buf)
2545 free(s->buf);
2546 s->buf = NULL;
2547 } else {
2548
2549 if (s->params.lines == 0 || s->params.bytes_per_line == 0) {
2550 DBG( _DBG_ERROR, "nothing to scan!\n" );
2551 usbDev_close( dev );
2552 sanei_access_unlock( dev->sane.name );
2553 return SANE_STATUS_INVAL;
2554 }
2555
2556 s->buf = realloc( s->buf, (s->params.lines) * s->params.bytes_per_line );
2557 if( NULL == s->buf ) {
2558 DBG( _DBG_ERROR, "realloc failed\n" );
2559 usbDev_close( dev );
2560 sanei_access_unlock( dev->sane.name );
2561 return SANE_STATUS_NO_MEM;
2562 }
2563 }
2564
2565 tsecs = (unsigned long)time(NULL);
2566 DBG( _DBG_INFO, "TIME START\n" );
2567
2568 DBG( _DBG_SANE_INIT, "local_sane_start done\n" );
2569 return SANE_STATUS_GOOD;
2570 }
2571
2572 /** initiate the scan process
2573 */
2574 SANE_Status
sane_start(SANE_Handle handle)2575 sane_start( SANE_Handle handle )
2576 {
2577 Plustek_Scanner *s = (Plustek_Scanner *)handle;
2578 Plustek_Device *dev = s->hw;
2579 SANE_Status status;
2580 int fds[2];
2581
2582 DBG( _DBG_SANE_INIT, "sane_start\n" );
2583
2584 if (s->scanning)
2585 return SANE_STATUS_DEVICE_BUSY;
2586
2587 /* in the end we wait until the calibration is done... */
2588 if (s->calibrating) {
2589 while (s->calibrating) {
2590 sleep(1);
2591 }
2592
2593 /* we have been cancelled? */
2594 if (cancelRead)
2595 return SANE_STATUS_CANCELLED;
2596 }
2597
2598 status = sane_get_parameters (handle, NULL);
2599 if (status != SANE_STATUS_GOOD) {
2600 DBG( _DBG_ERROR, "sane_get_parameters failed\n" );
2601 return status;
2602 }
2603
2604 status = local_sane_start(s, getScanMode(s));
2605 if (status != SANE_STATUS_GOOD) {
2606 return status;
2607 }
2608
2609 s->scanning = SANE_TRUE;
2610
2611 /*
2612 * everything prepared, so start the child process and a pipe to communicate
2613 * pipe --> fds[0]=read-fd, fds[1]=write-fd
2614 */
2615 if( pipe(fds) < 0 ) {
2616 DBG( _DBG_ERROR, "ERROR: could not create pipe\n" );
2617 s->scanning = SANE_FALSE;
2618 usbDev_close( dev );
2619 return SANE_STATUS_IO_ERROR;
2620 }
2621
2622 /* create reader routine as new process */
2623 s->bytes_read = 0;
2624 s->r_pipe = fds[0];
2625 s->w_pipe = fds[1];
2626 s->ipc_read_done = SANE_FALSE;
2627 s->reader_pid = sanei_thread_begin( reader_process, s );
2628
2629 cancelRead = SANE_FALSE;
2630
2631 if( !sanei_thread_is_valid (s->reader_pid) ) {
2632 DBG( _DBG_ERROR, "ERROR: could not start reader task\n" );
2633 s->scanning = SANE_FALSE;
2634 usbDev_close( dev );
2635 return SANE_STATUS_IO_ERROR;
2636 }
2637
2638 signal( SIGCHLD, sig_chldhandler );
2639
2640 if( sanei_thread_is_forked()) {
2641 close( s->w_pipe );
2642 s->w_pipe = -1;
2643 }
2644
2645 DBG( _DBG_SANE_INIT, "sane_start done\n" );
2646 return SANE_STATUS_GOOD;
2647 }
2648
2649 /** function to read the data from our child process
2650 */
2651 SANE_Status
sane_read(SANE_Handle handle,SANE_Byte * data,SANE_Int max_length,SANE_Int * length)2652 sane_read( SANE_Handle handle, SANE_Byte *data,
2653 SANE_Int max_length, SANE_Int *length )
2654 {
2655 Plustek_Scanner *s = (Plustek_Scanner*)handle;
2656 ssize_t nread;
2657 #ifdef USE_IPC
2658 static IPCDef ipc;
2659 unsigned char *buf;
2660 static unsigned long c = 0;
2661 #endif
2662
2663 *length = 0;
2664
2665 #ifdef USE_IPC
2666 /* first try and read IPC... */
2667 if( !s->ipc_read_done ) {
2668
2669 buf = (unsigned char*)&ipc;
2670 for( c = 0; c < sizeof(ipc); ) {
2671 nread = read( s->r_pipe, buf, sizeof(ipc));
2672 if( nread < 0 ) {
2673 if( EAGAIN != errno ) {
2674 do_cancel( s, SANE_TRUE );
2675 return SANE_STATUS_IO_ERROR;
2676 } else {
2677 return SANE_STATUS_GOOD;
2678 }
2679 } else {
2680 c += nread;
2681 buf += nread;
2682 if( c == sizeof(ipc)) {
2683 s->ipc_read_done = SANE_TRUE;
2684 break;
2685 }
2686 }
2687 }
2688 s->hw->transferRate = ipc.transferRate;
2689 DBG( _DBG_INFO, "IPC: Transferrate = %lu Bytes/s\n",
2690 ipc.transferRate );
2691 }
2692 #endif
2693 /* here we read all data from the driver... */
2694 nread = read( s->r_pipe, data, max_length );
2695 DBG( _DBG_READ, "sane_read - read %ld bytes\n", (long)nread );
2696 if (!(s->scanning)) {
2697 return do_cancel( s, SANE_TRUE );
2698 }
2699
2700 if( nread < 0 ) {
2701
2702 if( EAGAIN == errno ) {
2703
2704 /* if we already had red the picture, so it's okay and stop */
2705 if( s->bytes_read ==
2706 (unsigned long)(s->params.lines * s->params.bytes_per_line)) {
2707 sanei_thread_waitpid( s->reader_pid, 0 );
2708 sanei_thread_invalidate( s->reader_pid );
2709 s->scanning = SANE_FALSE;
2710 drvclose( s->hw );
2711 return close_pipe(s);
2712 }
2713
2714 /* else force the frontend to try again*/
2715 return SANE_STATUS_GOOD;
2716
2717 } else {
2718 DBG( _DBG_ERROR, "ERROR: errno=%d\n", errno );
2719 do_cancel( s, SANE_TRUE );
2720 return SANE_STATUS_IO_ERROR;
2721 }
2722 }
2723
2724 *length = nread;
2725 s->bytes_read += nread;
2726
2727 /* nothing red means that we're finished OR we had a problem... */
2728 if( 0 == nread ) {
2729
2730 drvclose( s->hw );
2731 s->exit_code = sanei_thread_get_status( s->reader_pid );
2732
2733 if( SANE_STATUS_GOOD != s->exit_code ) {
2734 close_pipe(s);
2735 return s->exit_code;
2736 }
2737 sanei_thread_invalidate( s->reader_pid );
2738 s->scanning = SANE_FALSE;
2739 return close_pipe(s);
2740 }
2741 return SANE_STATUS_GOOD;
2742 }
2743
2744 /** cancel the scanning process
2745 */
2746 void
sane_cancel(SANE_Handle handle)2747 sane_cancel( SANE_Handle handle )
2748 {
2749 Plustek_Scanner *s = (Plustek_Scanner *)handle;
2750
2751 DBG( _DBG_SANE_INIT, "sane_cancel\n" );
2752
2753 if (s->scanning || s->calibrating)
2754 do_cancel( s, SANE_FALSE );
2755 }
2756
2757 /** set the pipe to blocking/non blocking mode
2758 */
2759 SANE_Status
sane_set_io_mode(SANE_Handle handle,SANE_Bool non_blocking)2760 sane_set_io_mode( SANE_Handle handle, SANE_Bool non_blocking )
2761 {
2762 Plustek_Scanner *s = (Plustek_Scanner *)handle;
2763
2764 DBG( _DBG_SANE_INIT, "sane_set_io_mode: non_blocking=%d\n",non_blocking );
2765
2766 if ( !s->scanning ) {
2767 DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
2768 return SANE_STATUS_INVAL;
2769 }
2770
2771 if( -1 == s->r_pipe ) {
2772 DBG( _DBG_ERROR, "ERROR: not supported !\n" );
2773 return SANE_STATUS_UNSUPPORTED;
2774 }
2775
2776 if( fcntl (s->r_pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) {
2777 DBG( _DBG_ERROR, "ERROR: could not set to non-blocking mode !\n" );
2778 return SANE_STATUS_IO_ERROR;
2779 }
2780
2781 DBG( _DBG_SANE_INIT, "sane_set_io_mode done\n" );
2782 return SANE_STATUS_GOOD;
2783 }
2784
2785 /** return the descriptor if available
2786 */
2787 SANE_Status
sane_get_select_fd(SANE_Handle handle,SANE_Int * fd)2788 sane_get_select_fd( SANE_Handle handle, SANE_Int * fd )
2789 {
2790 Plustek_Scanner *s = (Plustek_Scanner *)handle;
2791
2792 DBG( _DBG_SANE_INIT, "sane_get_select_fd\n" );
2793
2794 if( !s->scanning ) {
2795 DBG( _DBG_ERROR, "ERROR: not scanning !\n" );
2796 return SANE_STATUS_INVAL;
2797 }
2798
2799 *fd = s->r_pipe;
2800
2801 DBG( _DBG_SANE_INIT, "sane_get_select_fd done\n" );
2802 return SANE_STATUS_GOOD;
2803 }
2804
2805 /* END PLUSTEK.C ............................................................*/
2806