• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*.............................................................................
2  * Project : SANE library for Plustek flatbed scanners.
3  *.............................................................................
4  */
5 
6 /** @file plustek-usbhw.c
7  *  @brief Functions to control the scanner hardware.
8  *
9  * Based on sources acquired from Plustek Inc.<br>
10  * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
11  *
12  * History:
13  * - 0.40 - starting version of the USB support
14  * - 0.41 - added EPSON1250 specific stuff
15  *        - added alternative usb_IsScannerReady function
16  * - 0.42 - added warmup stuff
17  *        - added UMAX 3400 stuff
18  *        - fixed problem with minimum wait time...
19  * - 0.43 - added usb_switchLamp for non-Plustek devices
20  * - 0.44 - added bStepsToReverse and active Pixelstart values
21  *        - to resetRegister function
22  *        - modified getLampStatus function for CIS devices
23  *        - added usb_Wait4Warmup()
24  *        - moved usb_IsEscPressed to this file
25  *        - added usb_switchLampX
26  *        - do now not reinitialized MISC I/O pins upon reset registers
27  * - 0.45 - added function usb_AdjustLamps() to tweak CIS lamp settings
28  *        - fixed NULL pointer problem in lamp-off ISR
29  *        - added usb_AdjustCISLampSettings()
30  *        - skipping warmup for CIS devices
31  * - 0.46 - fixed problem in usb_GetLampStatus for CIS devices, as we
32  *          read back reg[0x29] to wrong position
33  *          made it compile without itimer definitions
34  * - 0.47 - moved usb_HostSwap() and usb_Swap() to this file.
35  *        - fixed lampOff timer for systems w/o setitimer
36  *        - added lamp off adjustment for CIS devices
37  * - 0.48 - added usb_IsCISDevice()
38  *        - added usb_HasTPA()
39  *        - changed usb_Wait4Warmup()
40  *        - added usb_WaitPos()
41  *        - added usb_FillLampRegs() - sets also PWMDutyCylce now
42  *        - added UMAX3450 TPA autodetection
43  * - 0.49 - a_bRegs is now part of the device structure
44  *        - fixed problem in backtracking, when speedup is enabled
45  *        - added usb_UpdateButtonStatus()
46  * - 0.50 - added button support for Plustek/Genius devices
47  *        - changed behaviour of usb_IsScannerReady
48  *        - added special misc I/O setup for CIS devices (usb_ResetRegisters)
49  * - 0.51 - change usb_AdjustLamps() and use it now in usb_switchLamp()
50  *        - added usb_Wait4ScanSample() and usb_InCalibrationMode()
51  *        - tweaked EjectPaper to work correctly with the supported sheet-fed
52  *          devices
53  *        - fixed button handling for Plustek/Genius devices and added
54  *          some more debug output to that code path
55  * - 0.52 - changed DCapsDef, lamp -> misc_io
56  *        - hammer in output bit, when using misc io pins for lamp switching
57  *        - increased wait time for sheet-fed scanner (needed for Q-SCAN A6,
58  *          patch by Hiroshi Miura)
59  * .
60  * <hr>
61  * This file is part of the SANE package.
62  *
63  * This program is free software; you can redistribute it and/or
64  * modify it under the terms of the GNU General Public License as
65  * published by the Free Software Foundation; either version 2 of the
66  * License, or (at your option) any later version.
67  *
68  * This program is distributed in the hope that it will be useful, but
69  * WITHOUT ANY WARRANTY; without even the implied warranty of
70  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
71  * General Public License for more details.
72  *
73  * You should have received a copy of the GNU General Public License
74  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
75  *
76  * As a special exception, the authors of SANE give permission for
77  * additional uses of the libraries contained in this release of SANE.
78  *
79  * The exception is that, if you link a SANE library with other files
80  * to produce an executable, this does not by itself cause the
81  * resulting executable to be covered by the GNU General Public
82  * License.  Your use of that executable is in no way restricted on
83  * account of linking the SANE library code into it.
84  *
85  * This exception does not, however, invalidate any other reasons why
86  * the executable file might be covered by the GNU General Public
87  * License.
88  *
89  * If you submit changes to SANE to the maintainers to be included in
90  * a subsequent release, you agree by submitting the changes that
91  * those changes may be distributed with this exception intact.
92  *
93  * If you write modifications of your own for SANE, it is your choice
94  * whether to permit this exception to apply to your modifications.
95  * If you do not wish that, delete this exception notice.
96  * <hr>
97  */
98 #ifdef HAVE_SYS_TIME_H
99 #include <sys/time.h>
100 #endif
101 
102 #define DEV_LampReflection      1
103 #define DEV_LampTPA             2
104 #define DEV_LampAll             3
105 #define DEV_LampPositive        4
106 #define DEV_LampNegative        5
107 
108 #define WAIT_TIME_FOR_SCAN_SAMPLE 20   /* 20 seconds maximum wait time */
109 
110 
111 /** the NatSemi 983x is a big endian chip, and the line protocol data all
112  *  arrives big-endian.  This determines if we need to swap to host-order
113  */
114 static SANE_Bool
usb_HostSwap(void)115 usb_HostSwap( void )
116 {
117 	u_short        pattern  = 0xfeed; /* deadbeef */
118 	unsigned char *bytewise = (unsigned char *)&pattern;
119 
120 	if ( bytewise[0] == 0xfe ) {
121 		DBG( _DBG_READ, "We're big-endian!  No need to swap!\n" );
122 		return 0;
123 	}
124 	DBG( _DBG_READ, "We're little-endian!  NatSemi LM983x is big!\n" );
125 	DBG( _DBG_READ, "--> Must swap data!\n" );
126 	return 1;
127 }
128 
129 /** as the name says..
130  */
131 static void
usb_Swap(u_short * pw,u_long dwBytes)132 usb_Swap( u_short *pw, u_long dwBytes )
133 {
134 	for( dwBytes /= 2; dwBytes--; pw++ )
135 		_SWAP(((u_char*) pw)[0], ((u_char*)pw)[1]);
136 }
137 
138 /**
139  * This function is used to detect a cancel condition,
140  * our ESC key is the SIGUSR1 signal. It is sent by the backend when the
141  * cancel button has been pressed
142  *
143  * @param - none
144  * @return the function returns SANE_TRUE if a cancel condition has been
145  *  detected, if not, it returns SANE_FALSE
146  */
147 static SANE_Bool
usb_IsEscPressed(void)148 usb_IsEscPressed( void )
149 {
150 	sigset_t sigs;
151 
152 	sigpending( &sigs );
153 	if( sigismember( &sigs, SIGUSR1 )) {
154 		DBG( _DBG_INFO, "SIGUSR1 is pending --> Cancel detected\n" );
155 		return SANE_TRUE;
156 	}
157 
158 	return SANE_FALSE;
159 }
160 
161 /** usb_GetMotorSet
162  * according to the model, the function returns the address of
163  * the corresponding entry of the Motor table
164  */
165 static ClkMotorDef*
usb_GetMotorSet(eModelDef model)166 usb_GetMotorSet( eModelDef model )
167 {
168 	int i;
169 
170 	for( i = 0; i < MODEL_LAST; i++ ) {
171 		if( model == Motors[i].motorModel ) {
172 			return &(Motors[i]);
173 		}
174 	}
175 
176 	return NULL;
177 }
178 
179 /** switch motor on or off
180  * @param handle - handle to open USB device
181  * @param fOn    - SANE_TRUE means motor on, SANE_FALSE means motor off
182  * @return always SANE_TRUE
183  */
184 static SANE_Bool
usb_MotorOn(Plustek_Device * dev,SANE_Bool fOn)185 usb_MotorOn( Plustek_Device *dev, SANE_Bool fOn )
186 {
187 	if( fOn )
188 		dev->usbDev.a_bRegs[0x45] |= 0x10;
189 	else
190 		dev->usbDev.a_bRegs[0x45] &= ~0x10;
191 
192 	usbio_WriteReg( dev->fd, 0x45, dev->usbDev.a_bRegs[0x45] );
193 	return SANE_TRUE;
194 }
195 
196 /**
197  */
198 static SANE_Bool
usb_IsCISDevice(Plustek_Device * dev)199 usb_IsCISDevice( Plustek_Device *dev )
200 {
201 	return ( dev->usbDev.HwSetting.bReg_0x26 & _ONE_CH_COLOR );
202 }
203 
204 /**
205  */
206 static SANE_Bool
usb_IsSheetFedDevice(Plustek_Device * dev)207 usb_IsSheetFedDevice( Plustek_Device *dev )
208 {
209 	return ( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_SheetFed );
210 }
211 
212 /**
213  */
214 static SANE_Bool
usb_InCalibrationMode(Plustek_Device * dev)215 usb_InCalibrationMode( Plustek_Device *dev )
216 {
217 	if((dev->scanning.dwFlag & SCANFLAG_Calibration) == 0)
218 		return SANE_FALSE;
219 
220 	return SANE_TRUE;
221 }
222 
223 /** check if scanner is ready
224  */
225 static SANE_Bool
usb_IsScannerReady(Plustek_Device * dev)226 usb_IsScannerReady( Plustek_Device *dev )
227 {
228 	u_char         value;
229 	double         len;
230 	long           timeout;
231 	struct timeval t;
232 	SANE_Status    res;
233 
234 	/* time in s = 1000*scanner length in inches/max step speed/in */
235 	len = (dev->usbDev.Caps.Normal.Size.y/(double)_MEASURE_BASE) + 5;
236 	len = (1000.0 * len)/dev->usbDev.HwSetting.dMaxMoveSpeed;
237 	len /= 1000.0;
238 
239 	/* wait at least 10 seconds... */
240 	if( len < 10 )
241 		len = 10;
242 
243 	gettimeofday( &t, NULL);
244 	timeout = t.tv_sec + len;
245 
246 	do {
247 		res = usbio_ReadReg( dev->fd, 7, &value);
248 		if( res != SANE_STATUS_GOOD ) {
249 			sleep(1);
250 		} else {
251 			if( value == 0 ) {
252 				_UIO( usbio_ResetLM983x( dev ));
253 				return SANE_TRUE;
254 			}
255 
256 			if((value == 0) || (value >= 0x20) || (value == 0x03)) {
257 
258 				if( !usbio_WriteReg( dev->fd, 0x07, 0 )) {
259 					DBG( _DBG_ERROR, "Scanner not ready!!!\n" );
260 					return SANE_FALSE;
261 				}
262 				else {
263 					return SANE_TRUE;
264 				}
265 			}
266 		}
267 		gettimeofday( &t, NULL);
268 
269 	} while( t.tv_sec < timeout );
270 
271 	DBG( _DBG_ERROR, "Scanner not ready!!!\n" );
272 	return SANE_FALSE;
273 }
274 
275 /**
276  */
277 static SANE_Bool
usb_SensorAdf(Plustek_Device * dev)278 usb_SensorAdf( Plustek_Device *dev )
279 {
280 	u_char value;
281 
282 	if( usb_IsSheetFedDevice(dev))
283 		return SANE_FALSE;
284 
285 	usbio_ReadReg( dev->fd, 0x02, &value );
286 
287 	return (value & 0x20);
288 }
289 
290 /**
291  */
292 static SANE_Bool
usb_SensorPaper(Plustek_Device * dev)293 usb_SensorPaper( Plustek_Device *dev )
294 {
295 	DCapsDef   *sc   = &dev->usbDev.Caps;
296 	u_char val, mask = 0x02;
297 
298 	usbio_ReadReg( dev->fd, 0x02, &val );
299 
300 	if( usb_IsSheetFedDevice(dev))
301 		mask = _GET_PAPERSENSE_PORT(sc->misc_io);
302 
303 	return (val & mask);
304 }
305 
306 /** function for sheet-fed devices, to make sure, that there's
307  * something to scan
308  */
309 static SANE_Bool
usb_Wait4ScanSample(Plustek_Device * dev)310 usb_Wait4ScanSample( Plustek_Device *dev )
311 {
312 	struct timeval start_time, t2;
313 
314 	if( !usb_IsSheetFedDevice(dev))
315 		return SANE_TRUE;
316 
317 	DBG( _DBG_INFO2, "Waiting for something to scan...\n" );
318 	gettimeofday( &start_time, NULL );
319 	do {
320 
321 		gettimeofday( &t2, NULL );
322 		if( t2.tv_sec > start_time.tv_sec + WAIT_TIME_FOR_SCAN_SAMPLE ) {
323 			DBG( _DBG_ERROR, "Nothing to scan!!!\n" );
324 			return SANE_FALSE;
325 		}
326 		if( usb_IsEscPressed())
327 			return SANE_FALSE;
328 	}
329 	while( !usb_SensorPaper( dev ));
330 
331 	/* just a little delay, to make sure the paper is taken by the scanner */
332 	usleep(100* 1000);
333 	DBG( _DBG_INFO2, "... okay, scanning now!\n" );
334 	return SANE_TRUE;
335 }
336 
337 /** function to move the sensor and to speed it up to a certain speed until
338  *  the position is reached
339  */
340 static SANE_Bool
usb_WaitPos(Plustek_Device * dev,u_long to,SANE_Bool stay)341 usb_WaitPos( Plustek_Device *dev, u_long to, SANE_Bool stay )
342 {
343 	SANE_Bool      retval;
344 	u_char         value, mclk_div, mch;
345 	u_char         r[2];
346 	u_short        ffs, step, min_ffs;
347 	long           dwTicks;
348 	double         maxf, fac, speed;
349 	struct timeval start_time, t2;
350 
351 	HWDef         *hw   = &dev->usbDev.HwSetting;
352 	u_char        *regs = dev->usbDev.a_bRegs;
353 
354 	/* get current master clock divider */
355 	usbio_ReadReg( dev->fd, 0x08, &value );
356 	mclk_div = (value >> 1) + 1;
357 
358 	/* get current channel mode */
359 	usbio_ReadReg( dev->fd, 0x26, &value );
360 	mch = ((value & 0x07) > 1) ? 1:3;
361 
362 	/* calculate the current speed */
363 	ffs   = regs[0x48] * 256 + regs[0x49];
364 	speed = ((double)CRYSTAL_FREQ) /(double)((u_long)mclk_div * 32UL *
365 	                                (u_long)mch * (u_long)ffs * hw->wMotorDpi);
366 
367 	/* disabled ? */
368 	if((hw->dHighSpeed == 0.0) || (dev->adj.disableSpeedup != 0)) {
369 		min_ffs = 0xffff;
370 		maxf    = 0.0;
371 		if( !stay )
372 			return SANE_TRUE;
373 
374 	} else {
375 		min_ffs = (u_short)(CRYSTAL_FREQ /((u_long)mclk_div * 32UL *
376 		                   (u_long)mch * hw->dHighSpeed * hw->wMotorDpi));
377 		maxf = (ffs - min_ffs)/4;
378 		if( maxf > 100.0 )
379 			maxf = 100.0;
380 		if( maxf < 5.0 )
381 			maxf = 5.0;
382 		DBG( _DBG_INFO2, ">>>> CURRENT MCLK_DIV = %u\n", mclk_div );
383 		DBG( _DBG_INFO2, ">>>> MCH              = %u\n", mch );
384 		DBG( _DBG_INFO2, ">>>> FFS              = %u\n", ffs );
385 		DBG( _DBG_INFO2, ">>>> HIGH-SPEED       = %.3f (%.3f)\n",
386 		                  speed, hw->dHighSpeed);
387 		DBG( _DBG_INFO2, ">>>> MIN_FFS          = %u (%.3f)\n", min_ffs, maxf);
388 	}
389 
390 	gettimeofday( &start_time, NULL );
391 	dwTicks = start_time.tv_sec + to;
392 	step    = 1;
393 	retval  = SANE_FALSE;
394 
395 	for(;;) {
396 
397 		usleep( 1000 );
398 		_UIO( usbio_ReadReg( dev->fd, 0x07, &value ));
399 
400 		if (!value)
401 			return SANE_TRUE;
402 
403 		gettimeofday(&t2, NULL);
404 	    if( t2.tv_sec > dwTicks )
405 			break;
406 
407 		if( min_ffs != 0xffff ) {
408 
409 			fac = maxf/step;
410 			if( ffs ) {
411 				if((u_short)fac < ffs ) {
412 					ffs -= fac;
413 					if( ffs < min_ffs )
414 						ffs = min_ffs;
415 				} else {
416 					if(ffs != min_ffs )
417 						ffs = min_ffs;
418 					else
419 						ffs = 0;
420 				}
421 			}
422 
423 			if( ffs >= min_ffs ) {
424 				if((int)fac > 25 )
425 					usleep( 150 * 1000 );
426 
427 				r[0] = (u_char)(ffs >> 8);
428 				r[1] = (u_char)(ffs & 0xFF);
429 				sanei_lm983x_write(dev->fd, 0x48, r, 2, SANE_TRUE);
430 				if(ffs == min_ffs )
431 					ffs = 0;
432 			} else {
433 
434 				if( !stay ) {
435 					retval = SANE_TRUE;
436 					break;
437 				}
438 			}
439 			step++;
440 		}
441 	}
442 	return retval;
443 }
444 
445 /** function to move the sensor or if sheet-fed device, to move the paper.
446  * In moving backward-mode, the home sensor is always turned on.
447  * @param action - what to do
448  * @param steps  - steps to move, based on 300dpi, 0 means move forever
449  */
450 static SANE_Bool
usb_ModuleMove(Plustek_Device * dev,u_char action,u_long dwStep)451 usb_ModuleMove( Plustek_Device *dev, u_char action, u_long dwStep )
452 {
453 	SANE_Bool    retval, ejected;
454 	u_char       bReg2, reg7, mclk_div;
455 	u_short      wFastFeedStepSize;
456 	double       dMaxMoveSpeed;
457 	ClkMotorDef *clk;
458 	HWDef       *hw   = &dev->usbDev.HwSetting;
459 	u_char      *regs = dev->usbDev.a_bRegs;
460 
461 	if( action != MOVE_ToPaperSensor   &&
462 		action != MOVE_EjectAllPapers  &&
463 		action != MOVE_SkipPaperSensor &&
464 		action != MOVE_ToShading       && !dwStep ) {
465 
466 		return SANE_TRUE;
467 	}
468 
469 	if( !usb_IsScannerReady( dev )) {
470 		DBG( _DBG_ERROR, "Sensor-position NOT reached\n" );
471 		return SANE_FALSE;
472 	}
473 
474 	if( action == MOVE_EjectAllPapers ) {
475 
476 		double d = hw->dMaxMoveSpeed;
477 
478 		/* FIXME */
479 		if (hw->motorModel == MODEL_QSCAN_A6){
480 			DBG( _DBG_INFO2, "Q-SCAN-A6 may not be able to detect ejected papers\n");
481 			return SANE_TRUE;
482 		}
483 
484 		hw->dMaxMoveSpeed += 0.8; /* was 0.6 */
485 
486 		DBG( _DBG_INFO2, "Ejecting paper...\n" );
487 		retval  = SANE_TRUE;
488 		ejected = SANE_FALSE;
489 		do {
490 			if( usb_SensorPaper(dev)) {
491 				if (!usb_ModuleMove(dev,MOVE_SkipPaperSensor, 0 )) {
492 					hw->dMaxMoveSpeed = d;
493 					return SANE_FALSE;
494 				}
495 				ejected = SANE_TRUE;
496 			}
497 
498 			if( usb_SensorAdf(dev) &&
499 				!usb_ModuleMove(dev,MOVE_ToPaperSensor, 0 )) {
500 				hw->dMaxMoveSpeed = d;
501 				return SANE_FALSE;
502 			}
503 
504 			if( usb_IsEscPressed()) {
505 				retval = SANE_FALSE;
506 				break;
507 			}
508 		} while( usb_SensorPaper(dev));
509 
510 		/* when the paper is beyond the sensor, we move another 300 steps
511 		 * to make sure, that the scanned sheet is out of the scanner
512 		 * BUT: not at startup
513 		 */
514 		if (dev->initialized >= 0 || ejected) {
515 			DBG(_DBG_INFO2, "... MORE EJECT...\n");
516 			if(!usb_ModuleMove( dev, MOVE_Forward, 300 /* *3 */)) {
517 				hw->dMaxMoveSpeed = d;
518 				return SANE_FALSE;
519 			}
520 		}
521 
522 		usbio_WriteReg( dev->fd, 0x07, 0);
523 		usbio_WriteReg( dev->fd, 0x58, regs[0x58]);
524 
525 		usbio_ReadReg( dev->fd, 0x02, &bReg2 );
526 		hw->dMaxMoveSpeed = d;
527 		DBG( _DBG_INFO2, "...done\n" );
528 		return retval;
529 	}
530 
531 	usbio_WriteReg( dev->fd, 0x0a, 0 );
532 
533 	/* Compute fast feed step size, use equation 3 and equation 8 */
534 	dMaxMoveSpeed = hw->dMaxMoveSpeed;
535 
536 	if( action == MOVE_ToShading ) {
537 		if( hw->dMaxMoveSpeed > 0.5 )
538 			dMaxMoveSpeed = hw->dMaxMoveSpeed - 0.5;
539 	}
540 
541 	clk = usb_GetMotorSet( hw->motorModel );
542 
543 	mclk_div = clk->mclk_fast;
544 
545 	wFastFeedStepSize = (u_short)(CRYSTAL_FREQ /
546 	                    ((u_long)mclk_div * 8UL * 1 *
547 	                    dMaxMoveSpeed * 4 * hw->wMotorDpi));
548 
549 	regs[0x48] = (u_char)(wFastFeedStepSize >> 8);
550 	regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF);
551 
552 	dwStep = dwStep * hw->wMotorDpi / 300UL;
553 	regs[0x4a] = _HIBYTE(_LOWORD(dwStep));
554 	regs[0x4b] = _LOBYTE(_LOWORD(dwStep));
555 
556 	regs[0x45] |= 0x10;
557 
558 	DBG( _DBG_INFO2,"MotorDPI=%u, MaxMoveSpeed=%.3f, "
559 					"FFStepSize=%u, Steps=%lu\n", hw->wMotorDpi,
560 					hw->dMaxMoveSpeed, wFastFeedStepSize, dwStep );
561 	DBG( _DBG_INFO2, "MOTOR: "
562 					"PWM=0x%02x, PWM_DUTY=0x%02x 0x45=0x%02x "
563                     "0x48=0x%02x, 0x49=0x%02x \n",
564 					regs[0x56], regs[0x57], regs[0x45],
565 					regs[0x48], regs[0x49] );
566 
567 	DBG( _DBG_INFO2,"MCLK_FFW = %u --> 0x%02x\n", mclk_div, (mclk_div-1)*2 );
568 
569 	/* The setting for chassis moving is:
570 	 * MCLK divider = 6, 8 bits/pixel, HDPI divider = 12,
571 	 * no integration time adjustment and 1 channel grayscale
572 	 */
573 
574 	/* MCLK divider = 6 */
575 	if( !usbio_WriteReg(dev->fd, 0x08, (mclk_div-1)*2 /*0x0A*/))
576 		return SANE_FALSE;
577 
578 	/* 8 bits/pixel, HDPI divider = 12 */
579 	if( !usbio_WriteReg(dev->fd, 0x09, 0x1F))
580 		return SANE_FALSE;
581 
582 	/* Turn off integration time adjustment */
583 	if( !usbio_WriteReg(dev->fd, 0x19, 0))
584 		return SANE_FALSE;
585 
586 	/* 1 channel grayscale, green channel */
587 	if( !usbio_WriteReg(dev->fd, 0x26, 0x0C))
588 		return SANE_FALSE;
589 
590 	_UIO(sanei_lm983x_write(dev->fd, 0x48, &regs[0x48], 2, SANE_TRUE));
591 	_UIO(sanei_lm983x_write(dev->fd, 0x4A, &regs[0x4A], 2, SANE_TRUE));
592 
593 	/* disable home */
594 	if( !usbio_WriteReg(dev->fd, 0x58, regs[0x58] & ~7))
595 		return SANE_FALSE;
596 
597 	if( !usbio_WriteReg(dev->fd, 0x45, regs[0x45] ))
598 		return SANE_FALSE;
599 
600 	if( action == MOVE_Forward || action == MOVE_ToShading )
601 		reg7 = 5;
602 	else if( action == MOVE_Backward )
603 		reg7 = 6;
604 	else if( action == MOVE_ToPaperSensor || action == MOVE_EjectAllPapers ||
605 			 action == MOVE_SkipPaperSensor ) {
606 		reg7 = 1;
607 	} else {
608 		return SANE_TRUE;
609     }
610 
611 	retval = SANE_FALSE;
612 
613 	/* start the sensor... */
614 	if( usbio_WriteReg( dev->fd, 0x07, reg7 )) {
615 
616 		long           secs;
617 	    struct timeval start_time, t2;
618 
619 		/* at least we move 20 seconds before timeout... */
620 		gettimeofday( &start_time, NULL );
621 	    secs = start_time.tv_sec + 20;
622 
623 		if( action == MOVE_ToPaperSensor ) {
624 
625 			for(;;) {
626 
627 				if( usb_SensorPaper( dev )) {
628 					usbio_WriteReg( dev->fd, 0x07, 0 );
629 					usbio_WriteReg( dev->fd, 0x58, regs[0x58] );
630 					usbio_ReadReg ( dev->fd, 0x02, &bReg2 );
631 					return SANE_TRUE;
632 				}
633 
634 				gettimeofday(&t2, NULL);
635 				if( t2.tv_sec > secs )
636 					break;
637 			}
638 		} else if( action == MOVE_SkipPaperSensor ) {
639 
640 			for(;;) {
641 
642 				if( !usb_SensorPaper( dev )) {
643 					usbio_WriteReg( dev->fd, 0x07, 0 );
644 					usbio_WriteReg( dev->fd, 0x58, regs[0x58] );
645 					usbio_ReadReg ( dev->fd, 0x02, &bReg2 );
646 					return SANE_TRUE;
647 				}
648 
649 				gettimeofday(&t2, NULL);
650 				if( t2.tv_sec > secs )
651 					break;
652 			}
653 		} else {
654 
655 			retval = usb_WaitPos( dev, 200, SANE_TRUE );
656 		}
657 
658 		usbio_WriteReg( dev->fd, 0x58, regs[0x58] );
659 		usbio_ReadReg ( dev->fd, 0x02, &bReg2 );
660 	}
661 
662 	if( !retval )
663 		DBG( _DBG_ERROR, "Position NOT reached\n" );
664 	return retval;
665 }
666 
667 /**
668  */
669 static SANE_Bool
usb_ModuleToHome(Plustek_Device * dev,SANE_Bool fWait)670 usb_ModuleToHome( Plustek_Device *dev, SANE_Bool fWait )
671 {
672 	u_char    mclk_div;
673 	u_char    value;
674 	DCapsDef *scaps = &dev->usbDev.Caps;
675 	HWDef    *hw    = &dev->usbDev.HwSetting;
676 	u_char   *regs  = dev->usbDev.a_bRegs;
677 
678 	if( usb_IsSheetFedDevice(dev)) {
679 		return usb_ModuleMove( dev, MOVE_EjectAllPapers, 0 );
680 	}
681 
682 	/* Check if merlin is ready for setting command */
683 	usbio_WriteReg( dev->fd, 0x58, hw->bReg_0x58 );
684 	usbio_ReadReg ( dev->fd, 2, &value );
685 	if( value & 1 ) {
686 		dev->usbDev.fModFirstHome = SANE_FALSE;
687 		return SANE_TRUE;
688 	}
689 
690 	_UIO( usbio_ReadReg( dev->fd, 0x07, &value ));
691 
692 	if( dev->usbDev.fModFirstHome ) {
693 		dev->usbDev.fModFirstHome = SANE_FALSE;
694 		if( hw->motorModel != MODEL_Tokyo600 )
695 			usb_ModuleMove( dev, MOVE_Forward, hw->wMotorDpi / 2);
696 	}
697 
698 	/* if not homing, do it... */
699 	if( value != 2 ) {
700 
701 		u_short wFastFeedStepSize;
702 
703 		if( hw->motorModel == MODEL_Tokyo600 ) {
704 			usbio_WriteReg( dev->fd, 0x07, 0 );
705 		} else {
706 			_UIO( usbio_ResetLM983x( dev ));
707 			usleep(200*1000);
708 		}
709 
710 		if(!_IS_PLUSTEKMOTOR(hw->motorModel)) {
711 
712 			ClkMotorDef *clk;
713 
714 			clk = usb_GetMotorSet( hw->motorModel );
715 
716 			regs[0x56] = clk->pwm_fast;
717 			regs[0x57] = clk->pwm_duty_fast;
718 			mclk_div   = clk->mclk_fast;
719 
720 		} else {
721 
722 			mclk_div = 6;
723 
724 			if( scaps->OpticDpi.x == 1200 || scaps->bPCB == 2) {
725 				switch( hw->motorModel ) {
726 
727 				case MODEL_KaoHsiung:
728 				case MODEL_HuaLien:
729 				default:
730 					regs[0x56] = 1;
731 					regs[0x57] = 63;
732 					break;
733 				}
734 			} else { /* if(Device.Caps.OpticDpi.x == 600) */
735 
736 				switch( hw->motorModel ) {
737 
738 				case MODEL_Tokyo600:
739 					regs[0x56] = 4;
740 					regs[0x57] = 4;	/* 2; */
741 					break;
742 				case MODEL_HuaLien:
743 					if( dev->caps.dwFlag & SFLAG_ADF ) {
744 						regs[0x56] = 64;	/* 32; */
745 						regs[0x57] = 4;	/* 16; */
746 					} else {
747 						regs[0x56] = 32;
748 						regs[0x57] = 16;
749 					}
750 					break;
751 
752 				case MODEL_KaoHsiung:
753 				default:
754 					regs[0x56] = 64;
755 					regs[0x57] = 20;
756 					break;
757 				}
758 			}
759 		}
760 
761 		/* Compute fast feed step size, use equation 3 and equation 8
762 		 * assumptions: MCLK = 6, Lineratemode (CM=1)
763 		 */
764 		wFastFeedStepSize = (u_short)(CRYSTAL_FREQ / (mclk_div * 8 * 1 *
765 		                              hw->dMaxMotorSpeed * 4 * hw->wMotorDpi));
766 		regs[0x48] = (u_char)(wFastFeedStepSize >> 8);
767 		regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF);
768 		regs[0x4a] = 0;
769 		regs[0x4b] = 0;
770 
771 		regs[0x45] |= 0x10;
772 
773 		DBG( _DBG_INFO2,"MotorDPI=%u, MaxMotorSpeed=%.3f, FFStepSize=%u\n",
774 						hw->wMotorDpi, hw->dMaxMotorSpeed, wFastFeedStepSize );
775 		DBG( _DBG_INFO, "MOTOR: "
776 						"PWM=0x%02x, PWM_DUTY=0x%02x 0x45=0x%02x "
777                         "0x48=0x%02x, 0x49=0x%02x\n",
778 						regs[0x56], regs[0x57],
779 						regs[0x45], regs[0x48], regs[0x49] );
780 
781 		/* The setting for chassis moving is:
782 		 * MCLK divider = 6, 8 bits/pixel, HDPI divider = 12,
783 		 * no integration time adjustment and 1 channel grayscale
784 	 	 */
785 		/* MCLK divider = 6 */
786 		value = (u_char)((mclk_div-1) * 2);
787 
788 		DBG( _DBG_INFO, "MCLK_FFW = %u --> 0x%02x\n", mclk_div, value );
789 
790 		if( !usbio_WriteReg(dev->fd, 0x08, value))
791 			return SANE_FALSE;
792 
793 		/* 8 bits/pixel, HDPI divider = 12 */
794 		if( !usbio_WriteReg(dev->fd, 0x09, 0x1F))
795 			return SANE_FALSE;
796 
797 		/* Turn off integration time adjustment */
798 		if( !usbio_WriteReg(dev->fd, 0x19, 0))
799 			return SANE_FALSE;
800 
801 		/* 1 channel grayscale, green channel */
802 		if( !usbio_WriteReg(dev->fd, 0x26, 0x8C))
803 			return SANE_FALSE;
804 
805 		_UIO(sanei_lm983x_write(dev->fd, 0x48, &regs[0x48], 4, SANE_TRUE));
806 		_UIO(sanei_lm983x_write(dev->fd, 0x56, &regs[0x56], 3, SANE_TRUE));
807 
808 		if( !usbio_WriteReg(dev->fd, 0x45, regs[0x45]))
809 			return SANE_FALSE;
810 
811 		usbio_WriteReg(dev->fd, 0x0a, 0);
812 
813 		if( hw->motorModel == MODEL_HuaLien && scaps->OpticDpi.x == 600 )
814 			usleep(100 * 1000);
815 
816 		if( !usbio_WriteReg(dev->fd, 0x07, 2))
817 			return SANE_FALSE;
818 
819 #if 0
820 		if( hw->motorModel == MODEL_Tokyo600) {
821 
822 			u_long	dwSpeedUp = GetTickCount () + 250;
823 
824 			/* while(GetTickCount () < dwSpeedUp) */
825 			while((int)(dwSpeedUp - GetTickCount ()) > 0)
826 			{
827 				Sleep (10);
828 				if (!ReadRegister (0x07, &value))
829 					return FALSE;
830 				if (!value)
831 					return TRUE;
832 			}
833 			wFastFeedStepSize = (WORD)(CRYSTAL_FREQ /
834 			    (6UL * 8UL * 1 * Device.HwSetting.dMaxMotorSpeed * 4 *
835 				Device.HwSetting.wMotorDpi) * 60 / 78);
836 			regs[0x48] = (u_char)(wFastFeedStepSize >> 8);
837 			regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF);
838 			WriteRegisters(0x48, &regs[0x48], 2);
839 		}
840 #endif
841 	}
842 	return usb_WaitPos( dev, 150, fWait );
843 }
844 
845 /**
846  */
847 static SANE_Bool
usb_MotorSelect(Plustek_Device * dev,SANE_Bool fADF)848 usb_MotorSelect( Plustek_Device *dev, SANE_Bool fADF )
849 {
850 	DCapsDef *sCaps = &dev->usbDev.Caps;
851 	HWDef    *hw    = &dev->usbDev.HwSetting;
852 	u_char   *regs  = dev->usbDev.a_bRegs;
853 
854 	if(!_IS_PLUSTEKMOTOR(hw->motorModel)) {
855 		return SANE_TRUE;
856 	}
857 
858 	if( fADF ) {
859 
860 		if( sCaps->bCCD == kNEC3778 ) {
861 
862 			hw->wMotorDpi      = 300;
863 			hw->dMaxMotorSpeed = 1.5;
864 			hw->dMaxMoveSpeed  = 1.5;
865 			sCaps->OpticDpi.y  = 600;
866 		}
867 		regs[0x5b] |= 0x80;
868 
869 	} else {
870 
871 		if( sCaps->bCCD == kNEC3778 ) {
872 
873 			hw->wMotorDpi      = 600;
874 			hw->dMaxMotorSpeed = 1.1;
875 			hw->dMaxMoveSpeed  = 0.9;
876 			sCaps->OpticDpi.y  = 1200;
877 		}
878 		regs[0x5b] &= ~0x80;
879 	}
880 
881 	/* To stop the motor moving */
882 	usbio_WriteReg( dev->fd, 0x07, 0 );
883 	usleep(10 * 1000);
884 
885 	usbio_WriteReg( dev->fd, 0x5b, regs[0x5b] );
886 	return SANE_TRUE;
887 }
888 
889 /** function to adjust the lamp settings of a CIS device without tweaking
890  *  the driver-device settings
891  * @param dev - our almitghty device structure
892  * @param on  - switch the lamp on or off
893  */
894 static SANE_Bool
usb_AdjustLamps(Plustek_Device * dev,SANE_Bool on)895 usb_AdjustLamps( Plustek_Device *dev, SANE_Bool on )
896 {
897 	HWDef  *hw   = &dev->usbDev.HwSetting;
898 	u_char *regs = dev->usbDev.a_bRegs;
899 
900 	if( !usb_IsCISDevice(dev))
901 		return SANE_TRUE;
902 
903 	DBG(_DBG_INFO2, "usb_AdjustLamps(%u)\n", on );
904 
905 	if( on ) {
906 		regs[0x2c] = _HIBYTE(hw->red_lamp_on);
907 		regs[0x2d] = _LOBYTE(hw->red_lamp_on);
908 		regs[0x2e] = _HIBYTE(hw->red_lamp_off);
909 		regs[0x2f] = _LOBYTE(hw->red_lamp_off);
910 
911 		regs[0x30] = _HIBYTE(hw->green_lamp_on);
912 		regs[0x31] = _LOBYTE(hw->green_lamp_on);
913 		regs[0x32] = _HIBYTE(hw->green_lamp_off);
914 		regs[0x33] = _LOBYTE(hw->green_lamp_off);
915 
916 		regs[0x34] = _HIBYTE(hw->blue_lamp_on);
917 		regs[0x35] = _LOBYTE(hw->blue_lamp_on);
918 		regs[0x36] = _HIBYTE(hw->blue_lamp_off);
919 		regs[0x37] = _LOBYTE(hw->blue_lamp_off);
920 
921 	} else {
922 		memset( &regs[0x2c], 0, 12 );
923 
924 		regs[0x2c] = 0x3f;
925 		regs[0x2d] = 0xff;
926 		regs[0x30] = 0x3f;
927 		regs[0x31] = 0xff;
928 		regs[0x34] = 0x3f;
929 		regs[0x35] = 0xff;
930 	}
931 
932 	return sanei_lm983x_write( dev->fd, 0x2c,
933 	                           &regs[0x2c], 0x37-0x2c+1, SANE_TRUE );
934 }
935 
936 /**
937  */
938 static void
usb_AdjustCISLampSettings(Plustek_Device * dev,SANE_Bool on)939 usb_AdjustCISLampSettings( Plustek_Device *dev, SANE_Bool on )
940 {
941 	HWDef *hw = &dev->usbDev.HwSetting;
942 
943 	if( !usb_IsCISDevice(dev))
944 		return;
945 
946 	DBG( _DBG_INFO2, "AdjustCISLamps(%u)\n", on );
947 
948 	if((dev->scanning.sParam.bDataType == SCANDATATYPE_Gray) ||
949 	   (dev->scanning.sParam.bDataType == SCANDATATYPE_BW)) {
950 
951 		DBG( _DBG_INFO2, " * setting mono mode\n" );
952 		hw->bReg_0x29 = hw->illu_mono.mode;
953 
954 		memcpy( &hw->red_lamp_on,
955 				&hw->illu_mono.red_lamp_on, sizeof(u_short) * 6 );
956 
957 	} else {
958 
959 		DBG( _DBG_INFO2, " * setting color mode\n" );
960 		hw->bReg_0x29 = hw->illu_color.mode;
961 
962 		memcpy( &hw->red_lamp_on,
963 				&hw->illu_color.red_lamp_on, sizeof(u_short) * 6 );
964 	}
965 
966 	if( !on ) {
967 
968 		hw->red_lamp_on    = 0x3fff;
969 		hw->red_lamp_off   = 0;
970 		hw->green_lamp_on  = 0x3fff;
971 		hw->green_lamp_off = 0;
972 		hw->blue_lamp_on   = 0x3fff;
973 		hw->blue_lamp_off  = 0;
974 	} else {
975 
976 		if( dev->adj.rlampoff > 0 ) {
977 			hw->red_lamp_off = dev->adj.rlampoff;
978 
979 			if( hw->red_lamp_off > 0x3fff )
980 				hw->red_lamp_off = 0x3fff;
981 			DBG( _DBG_INFO2,
982 			     " * red_lamp_off adjusted: %u\n", hw->red_lamp_off );
983 		}
984 
985 		if( dev->adj.glampoff > 0 ) {
986 			hw->green_lamp_off = dev->adj.glampoff;
987 
988 			if( hw->green_lamp_off > 0x3fff )
989 				hw->green_lamp_off = 0x3fff;
990 			DBG( _DBG_INFO2,
991 			     " * green_lamp_off adjusted: %u\n", hw->green_lamp_off );
992 		}
993 
994 		if( dev->adj.blampoff > 0 ) {
995 			hw->blue_lamp_off = dev->adj.blampoff;
996 
997 			if( hw->blue_lamp_off > 0x3fff )
998 				hw->blue_lamp_off = 0x3fff;
999 			DBG( _DBG_INFO2,
1000 			     " * blue_lamp_off adjusted: %u\n", hw->blue_lamp_off );
1001 		}
1002 	}
1003 
1004 	dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29;
1005 	usb_AdjustLamps( dev, on );
1006 }
1007 
1008 /** according to the flag field, we return the register and
1009  * it's mask to turn on/off the lamp.
1010  * @param flag - field to check
1011  * @param reg  - pointer to a var to receive the register value
1012  * @param msk  - pointer to a var to receive the mask value
1013  * @return Nothing
1014  */
1015 static void
usb_GetLampRegAndMask(u_long flag,SANE_Byte * reg,SANE_Byte * msk)1016 usb_GetLampRegAndMask( u_long flag, SANE_Byte *reg, SANE_Byte *msk )
1017 {
1018 	if( _MIO6 == ( _MIO6 & flag )) {
1019 		*reg = 0x5b;
1020 		*msk = 0x80;
1021 
1022 	} else if( _MIO5 == ( _MIO5 & flag )) {
1023 		*reg = 0x5b;
1024 		*msk = 0x08;
1025 
1026 	} else if( _MIO4 == ( _MIO4 & flag )) {
1027 		*reg = 0x5a;
1028 		*msk = 0x80;
1029 
1030 	} else if( _MIO3 == ( _MIO3 & flag )) {
1031 		*reg = 0x5a;
1032 		*msk = 0x08;
1033 
1034 	} else if( _MIO2 == ( _MIO2 & flag )) {
1035 		*reg = 0x59;
1036 		*msk = 0x80;
1037 
1038 	} else if( _MIO1 == ( _MIO1 & flag )) {
1039 		*reg = 0x59;
1040 		*msk = 0x08;
1041 
1042 	} else {
1043 		*reg = 0;
1044 		*msk = 0;
1045 	}
1046 }
1047 
1048 /** usb_GetLampStatus
1049  * This function returns the current lamp in use.
1050  * For non Plustek devices, it always returns DEV_LampReflection.
1051  * @param dev  - pointer to our device structure,
1052  *               it should contain all we need
1053  * @return - 0 if the scanner hasn't been used before, DEV_LampReflection
1054  *           for the normal lamp, or DEV_LampTPA for negative/transparency
1055  *           lamp
1056  */
1057 static int
usb_GetLampStatus(Plustek_Device * dev)1058 usb_GetLampStatus( Plustek_Device *dev )
1059 {
1060 	int         iLampStatus = 0;
1061 	u_char     *regs = dev->usbDev.a_bRegs;
1062 	HWDef      *hw   = &dev->usbDev.HwSetting;
1063 	DCapsDef   *sc   = &dev->usbDev.Caps;
1064 	SANE_Byte   reg, msk, val;
1065 
1066 	if( NULL == hw ) {
1067 		DBG( _DBG_ERROR, "NULL-Pointer detected: usb_GetLampStatus()\n" );
1068 		return -1;
1069 	}
1070 
1071 	/* do we use the misc I/O pins for switching the lamp ? */
1072 	if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag ) {
1073 
1074 		usb_GetLampRegAndMask( sc->misc_io, &reg, &msk );
1075 
1076 		if( 0 == reg ) {
1077 			usbio_ReadReg( dev->fd, 0x29, &reg );
1078 			if( reg & 3 )
1079 				iLampStatus |= DEV_LampReflection;
1080 
1081 		} else {
1082 
1083 			/* check if the lamp is on */
1084 			usbio_ReadReg( dev->fd, reg, &val );
1085 
1086 			DBG( _DBG_INFO2, "LAMP-REG[0x%02x] = 0x%02x (msk=0x%02x)\n",
1087 			                  reg,val,msk);
1088 			if( val & msk )
1089 				iLampStatus |= DEV_LampReflection;
1090 
1091 			/* if the device supports a TPA, we check this here */
1092 			if( sc->wFlags & DEVCAPSFLAG_TPA ) {
1093 
1094 				usb_GetLampRegAndMask( _GET_TPALAMP(sc->misc_io), &reg, &msk );
1095 				if (reg) {
1096 					usbio_ReadReg( dev->fd, reg, &val );
1097 					DBG( _DBG_INFO2, "TPA-REG[0x%02x] = 0x%02x (msk=0x%02x)\n",
1098 					                  reg,val,msk);
1099 					if( val & msk )
1100 						iLampStatus |= DEV_LampTPA;
1101 				}
1102 			}
1103 
1104 			/* CanoScan D660U extra vaganza... */
1105 			if((dev->usbDev.vendor == 0x04A9) && (dev->usbDev.product==0x2208)) {
1106 				sanei_lm983x_read( dev->fd, 0x29, &regs[0x29], 3, SANE_TRUE );
1107 				DBG( _DBG_INFO, "[29]=0x%02x, [2A]=0x%02x, [2B]=0x%02x\n",
1108 				                regs[0x29], regs[0x2a], regs[0x2b] );
1109 			}
1110 		}
1111 	} else {
1112 		sanei_lm983x_read(dev->fd, 0x29,&regs[0x29],0x37-0x29+1,SANE_TRUE);
1113 
1114 		if((regs[0x29] & 3) == 1) {
1115 
1116 /* HEINER: BETTER define register to check ! */
1117 
1118 			if(!_IS_PLUSTEKMOTOR(hw->motorModel)) {
1119 				iLampStatus |= DEV_LampReflection;
1120 
1121 			} else {
1122 
1123 				if((regs[0x2e] * 256 + regs[0x2f]) > hw->wLineEnd )
1124 					iLampStatus |= DEV_LampReflection;
1125 
1126 				if((regs[0x36] * 256 + regs[0x37]) > hw->wLineEnd )
1127 					iLampStatus |= DEV_LampTPA;
1128 			}
1129 		}
1130 	}
1131 
1132 	DBG( _DBG_INFO, "LAMP-STATUS: 0x%08x (%s)\n",
1133 	                 iLampStatus, iLampStatus?"on":"off" );
1134 	return iLampStatus;
1135 }
1136 
1137 /** usb_switchLampX
1138  * used for all devices that use some misc I/O pins to switch the lamp
1139  */
1140 static SANE_Bool
usb_switchLampX(Plustek_Device * dev,SANE_Bool on,SANE_Bool tpa)1141 usb_switchLampX( Plustek_Device *dev, SANE_Bool on, SANE_Bool tpa )
1142 {
1143 	SANE_Byte reg, msk;
1144 	DCapsDef *sc   = &dev->usbDev.Caps;
1145 	u_char   *regs = dev->usbDev.a_bRegs;
1146 
1147 	if( tpa )
1148 		usb_GetLampRegAndMask( _GET_TPALAMP(sc->misc_io), &reg, &msk );
1149 	else
1150 		usb_GetLampRegAndMask( sc->misc_io, &reg, &msk );
1151 
1152 	if( 0 == reg )
1153 		return SANE_FALSE; /* no need to switch something */
1154 
1155 	DBG( _DBG_INFO, "usb_switchLampX(ON=%u,TPA=%u)\n", on, tpa );
1156 
1157 	if( on ) {
1158 		/* in fact the output bit should be set by the default settings
1159 		 * but we make sure, that it is set anyway...
1160 		 */
1161 		if (msk & 0x08)
1162 			msk |= 0x01;
1163 		else
1164 			msk |= 0x10;
1165 		regs[reg] |= msk;
1166 	} else {
1167 		regs[reg] &= ~msk;
1168 	}
1169 
1170 	DBG( _DBG_INFO, "Switch Lamp: %u, regs[0x%02x] = 0x%02x\n",
1171 	                on, reg, regs[reg] );
1172 	usbio_WriteReg( dev->fd, reg, regs[reg] );
1173 	return SANE_TRUE;
1174 }
1175 
1176 /** usb_switchLamp
1177  * used for all devices that use some misc I/O pins to switch the lamp
1178  */
1179 static SANE_Bool
usb_switchLamp(Plustek_Device * dev,SANE_Bool on)1180 usb_switchLamp( Plustek_Device *dev, SANE_Bool on )
1181 {
1182 	SANE_Bool result;
1183 
1184 	if((dev->scanning.sParam.bSource == SOURCE_Negative) ||
1185 	   (dev->scanning.sParam.bSource == SOURCE_Transparency)) {
1186 		result = usb_switchLampX( dev, on, SANE_TRUE );
1187 	} else {
1188 		result = usb_switchLampX( dev, on, SANE_FALSE );
1189 	}
1190 
1191 	/* to switch off CIS, we need to tweak the lampoff/on regs */
1192 	usb_AdjustLamps( dev, on );
1193 	return result;
1194 }
1195 
1196 /** usb_LedOn
1197  */
1198 static void
usb_LedOn(Plustek_Device * dev,SANE_Bool fOn)1199 usb_LedOn( Plustek_Device *dev, SANE_Bool fOn )
1200 {
1201 	u_char value;
1202 
1203 	if( dev->usbDev.HwSetting.motorModel != MODEL_HuaLien )
1204 		return;
1205 
1206 	value = dev->usbDev.a_bRegs[0x0d];
1207 
1208 	if( fOn )
1209 		value |= 0x10;
1210 	else
1211 		value &= ~0x10;
1212 
1213 	dev->usbDev.a_bRegs[0x0d] = value;
1214 	usbio_WriteReg( dev->fd, 0x0d, value );
1215 }
1216 
1217 /** usb_FillLampRegs
1218  * set all the registers controlling the lamps
1219  */
1220 static void
usb_FillLampRegs(Plustek_Device * dev)1221 usb_FillLampRegs( Plustek_Device *dev )
1222 {
1223 	HWDef  *hw   = &dev->usbDev.HwSetting;
1224 	u_char *regs = dev->usbDev.a_bRegs;
1225 
1226 	regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleLow );
1227 	regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleLow );
1228 
1229 	regs[0x2c] = _HIBYTE( hw->red_lamp_on );
1230 	regs[0x2d] = _LOBYTE( hw->red_lamp_on );
1231 	regs[0x2e] = _HIBYTE( hw->red_lamp_off);
1232 	regs[0x2f] = _LOBYTE( hw->red_lamp_off);
1233 
1234 	regs[0x30] = _HIBYTE( hw->green_lamp_on );
1235 	regs[0x31] = _LOBYTE( hw->green_lamp_on );
1236 	regs[0x32] = _HIBYTE( hw->green_lamp_off);
1237 	regs[0x33] = _LOBYTE( hw->green_lamp_off);
1238 
1239 	regs[0x34] = _HIBYTE( hw->blue_lamp_on );
1240 	regs[0x35] = _LOBYTE( hw->blue_lamp_on );
1241 	regs[0x36] = _HIBYTE( hw->blue_lamp_off);
1242 	regs[0x37] = _LOBYTE( hw->blue_lamp_off);
1243 }
1244 
1245 /** usb_LampOn
1246  */
1247 static SANE_Bool
usb_LampOn(Plustek_Device * dev,SANE_Bool fOn,SANE_Bool fResetTimer)1248 usb_LampOn( Plustek_Device *dev, SANE_Bool fOn, SANE_Bool fResetTimer )
1249 {
1250 	DCapsDef      *sc          = &dev->usbDev.Caps;
1251 	ScanDef       *scanning    = &dev->scanning;
1252 	HWDef         *hw          = &dev->usbDev.HwSetting;
1253 	u_char        *regs        = dev->usbDev.a_bRegs;
1254 	int            iLampStatus = usb_GetLampStatus( dev );
1255 	int            lampId      = -1;
1256 	struct timeval t;
1257 
1258 	if( NULL == scanning ) {
1259 		DBG( _DBG_ERROR, "NULL-Pointer detected: usb_LampOn()\n" );
1260 		return SANE_FALSE;
1261 	}
1262 
1263 	switch( scanning->sParam.bSource ) {
1264 
1265 	case SOURCE_Reflection:
1266 	case SOURCE_ADF:
1267 		lampId = DEV_LampReflection;
1268 		break;
1269 
1270 	case SOURCE_Transparency:
1271 	case SOURCE_Negative:
1272 		lampId = DEV_LampTPA;
1273 		break;
1274 	}
1275 
1276 	if( fOn ) {
1277 
1278 		if( iLampStatus != lampId ) {
1279 
1280 			DBG( _DBG_INFO, "Switching Lamp on\n" );
1281 
1282 /* here we might have to switch off the TPA/Main lamp before
1283  * using the other one
1284  */
1285 			if( lampId != dev->usbDev.currentLamp ) {
1286 				if( dev->usbDev.currentLamp == DEV_LampReflection )
1287 					usb_switchLampX( dev, SANE_FALSE, SANE_FALSE );
1288 				else
1289 					usb_switchLampX( dev, SANE_FALSE, SANE_TRUE );
1290 			}
1291 
1292 			memset( &regs[0x29], 0, (0x37-0x29+1));
1293 
1294 			regs[0x29] = hw->bReg_0x29;
1295 
1296 			if( !usb_switchLamp(dev, SANE_TRUE )) {
1297 
1298 				if( lampId == DEV_LampReflection ) {
1299 					regs[0x2e] = 16383 / 256;
1300 					regs[0x2f] = 16383 % 256;
1301 				}
1302 
1303 				if( lampId == DEV_LampTPA ) {
1304 					regs[0x36] = 16383 / 256;
1305 					regs[0x37] = 16383 % 256;
1306 				}
1307 			}
1308 
1309 			if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag )
1310 				usb_FillLampRegs( dev );
1311 
1312 			sanei_lm983x_write( dev->fd, 0x29,
1313 			                    &regs[0x29], 0x37-0x29+1, SANE_TRUE );
1314 			if( lampId != dev->usbDev.currentLamp ) {
1315 
1316 				dev->usbDev.currentLamp = lampId;
1317 
1318 				if( fResetTimer ) {
1319 
1320 					gettimeofday( &t, NULL );
1321 					dev->usbDev.dwTicksLampOn = t.tv_sec;
1322 					DBG( _DBG_INFO, "Warmup-Timer started\n" );
1323 				}
1324 			}
1325 		}
1326 
1327 	} else {
1328 
1329 		int iStatusChange = iLampStatus & ~lampId;
1330 
1331 		if( iStatusChange != iLampStatus ) {
1332 
1333 			DBG( _DBG_INFO, "Switching Lamp off\n" );
1334 
1335 			memset( &regs[0x29], 0, 0x37-0x29+1 );
1336 			if( !usb_switchLamp(dev, SANE_FALSE )) {
1337 
1338 				if( iStatusChange & DEV_LampReflection ) {
1339 					regs[0x2e] = 16383 / 256;
1340 					regs[0x2f] = 16383 % 256;
1341 				}
1342 
1343 				if( iStatusChange & DEV_LampTPA ) {
1344 					regs[0x36] = 16383 / 256;
1345 					regs[0x37] = 16383 % 256;
1346 				}
1347 			}
1348 
1349 			if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag )
1350 				usb_FillLampRegs( dev );
1351 
1352 			sanei_lm983x_write( dev->fd, 0x29,
1353 			                    &regs[0x29], 0x37-0x29+1, SANE_TRUE );
1354 		}
1355 	}
1356 	if( usb_GetLampStatus(dev))
1357 		usb_LedOn( dev, SANE_TRUE );
1358 	else
1359 		usb_LedOn( dev, SANE_FALSE );
1360 	return SANE_TRUE;
1361 }
1362 
1363 /** Function to preset the registers for the specific device, which
1364  * should never change during the whole operation
1365  * Affected registers:<br>
1366  * 0x0b - 0x0e - Sensor settings - directly from the HWDef<br>
1367  * 0x0f - 0x18 - Sensor Configuration - directly from the HwDef<br>
1368  * 0x1a - 0x1b - Stepper Phase Correction<br>
1369  * 0x20 - 0x21 - Line End<br>
1370  * 0x21 - 0x22 - Data Pixel start<br>
1371  * 0x23 - 0x24 - Data Pixel end<br>
1372  * 0x45        - Stepper Motor Mode<br>
1373  * 0x4c - 0x4d - Full Steps to Scan after PAPER SENSE 2 trips<br>
1374  * 0x50        - Steps to reverse when buffer is full<br>
1375  * 0x51        - Acceleration Profile<br>
1376  * 0x54 - 0x5e - Motor Settings, Paper-Sense Settings and Misc I/O<br>
1377  *
1378  * @param dev    - pointer to our device structure,
1379  *                 it should contain all we need
1380  * @return - Nothing
1381  */
1382 static void
usb_ResetRegisters(Plustek_Device * dev)1383 usb_ResetRegisters( Plustek_Device *dev )
1384 {
1385 	int linend;
1386 
1387 	HWDef  *hw   = &dev->usbDev.HwSetting;
1388 	u_char *regs = dev->usbDev.a_bRegs;
1389 
1390 	DBG( _DBG_INFO, "RESETTING REGISTERS(%i) - 0x%02x\n",
1391 	                dev->initialized, (int) sizeof(dev->usbDev.a_bRegs));
1392 	memset( regs, 0, sizeof(dev->usbDev.a_bRegs));
1393 
1394 	memcpy( regs+0x0b, &hw->bSensorConfiguration, 4 );
1395 	memcpy( regs+0x0f, &hw->bReg_0x0f_Color, 10 );
1396 	regs[0x1a] = _HIBYTE( hw->StepperPhaseCorrection );
1397 	regs[0x1b] = _LOBYTE( hw->StepperPhaseCorrection );
1398 
1399 /* HEINER: CHECK WHY THIS has been disabled*/
1400 #if 0
1401 	regs[0x1c] = hw->bOpticBlackStart;
1402 	regs[0x1d] = hw->bOpticBlackEnd;
1403 
1404 	regs[0x1e] = _HIBYTE( hw->wActivePixelsStart );
1405 	regs[0x1f] = _LOBYTE( hw->wActivePixelsStart );
1406 #endif
1407 	regs[0x20] = _HIBYTE( hw->wLineEnd );
1408 	regs[0x21] = _LOBYTE( hw->wLineEnd );
1409 
1410 	regs[0x22] = _HIBYTE( hw->bOpticBlackStart );
1411 	regs[0x23] = _LOBYTE( hw->bOpticBlackStart );
1412 
1413 	linend = hw->bOpticBlackStart + hw->wLineEnd;
1414 	if( linend < (hw->wLineEnd-20))
1415 		linend = hw->wLineEnd-20;
1416 
1417 	regs[0x24] = _HIBYTE( linend );
1418 	regs[0x25] = _LOBYTE( linend );
1419 
1420 	regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleHigh );
1421 	regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleHigh );
1422 
1423 	regs[0x45] = hw->bReg_0x45;
1424 	regs[0x4c] = _HIBYTE( hw->wStepsAfterPaperSensor2 );
1425 	regs[0x4d] = _LOBYTE( hw->wStepsAfterPaperSensor2 );
1426 	regs[0x50] = hw->bStepsToReverse;
1427 	regs[0x51] = hw->bReg_0x51;
1428 
1429 	/* if already initialized, we ignore the MISC I/O settings as
1430 	 * they are used to determine the current lamp settings...
1431 	 */
1432 	if( dev->initialized >= 0 ) {
1433 
1434 		DBG( _DBG_INFO2, "USING MISC I/O settings\n" );
1435 		memcpy( regs+0x54, &hw->bReg_0x54, 0x58 - 0x54 + 1 );
1436 		regs[0x5c] = hw->bReg_0x5c;
1437 		regs[0x5d] = hw->bReg_0x5d;
1438 		regs[0x5e] = hw->bReg_0x5e;
1439 		sanei_lm983x_read( dev->fd, 0x59, &regs[0x59], 3, SANE_TRUE );
1440 
1441 	} else {
1442 
1443 		DBG( _DBG_INFO2, "SETTING THE MISC I/Os\n" );
1444 		memcpy( regs+0x54, &hw->bReg_0x54, 0x5e - 0x54 + 1 );
1445 
1446 		if( usb_IsCISDevice( dev )) {
1447 
1448 			/* this sequence seems to be needed at least for the
1449 			 * CanoScan devices to work properly after power-up
1450 			 */
1451 			sanei_lm983x_write_byte( dev->fd, 0x5b, regs[0x5b] );
1452 
1453 			/* At least CanoScan N650U can have a problem with writing
1454 			 * to register 0x59 due XHCI USB controller is too
1455 			 * fast for him. Simulate EHCI USB controller's
1456 			 * behavior here - wait 1ms.
1457 			 */
1458 			usleep(1000);
1459 
1460 			sanei_lm983x_write_byte( dev->fd, 0x59, regs[0x59] );
1461 			sanei_lm983x_write_byte( dev->fd, 0x5a, regs[0x5a] );
1462 		} else {
1463 			sanei_lm983x_write( dev->fd, 0x59, &regs[0x59], 3, SANE_TRUE );
1464 		}
1465 	}
1466 	DBG( _DBG_INFO, "MISC I/O after RESET: 0x%02x, 0x%02x, 0x%02x\n",
1467 	                        regs[0x59], regs[0x5a], regs[0x5b] );
1468 }
1469 
1470 /** function which checks if we are already in home position or not.
1471  *
1472  */
1473 static SANE_Bool
usb_SensorStatus(Plustek_Device * dev)1474 usb_SensorStatus( Plustek_Device *dev )
1475 {
1476 	u_char value;
1477 	HWDef *hw = &dev->usbDev.HwSetting;
1478 
1479 /* HEINER: Maybe needed to avoid recalibration!!! */
1480 #if 0
1481 	if( dev->scanning.fCalibrated )
1482 		return SANE_TRUE;
1483 #endif
1484 
1485 	/* read the status register */
1486 	_UIO( usbio_ReadReg( dev->fd, 2, &value ));
1487 	if( value & 1 ) {
1488 
1489 		_UIO( usbio_ReadReg( dev->fd, 0x7, &value));
1490 		if( value ) {
1491 
1492 			usbio_WriteReg( dev->fd, 0x07, 0 );
1493 			usbio_WriteReg( dev->fd, 0x07, 0x20 );
1494 			usbio_WriteReg( dev->fd, 0x07, 0 );
1495 
1496 			sanei_lm983x_write( dev->fd, 0x58,
1497 								&hw->bReg_0x58, 0x5b-0x58+1, SANE_TRUE );
1498 			usbio_ReadReg( dev->fd, 2, &value );
1499 			usbio_ReadReg( dev->fd, 2, &value );
1500 		}
1501 
1502 		usb_MotorOn( dev, SANE_FALSE );
1503 		return SANE_TRUE;
1504 	}
1505 
1506 	_UIO( usbio_ReadReg( dev->fd, 0x7, &value ));
1507 
1508 	if( !(value & 2)) {
1509 		usb_ModuleToHome( dev, SANE_FALSE );
1510 	}
1511 
1512 	return SANE_FALSE;
1513 }
1514 
1515 /**
1516  */
1517 static void
usb_LampSwitch(Plustek_Device * dev,SANE_Bool sw)1518 usb_LampSwitch( Plustek_Device *dev, SANE_Bool sw )
1519 {
1520 	int handle = -1;
1521 
1522 	if( -1 == dev->fd ) {
1523 
1524 		if( SANE_STATUS_GOOD == sanei_usb_open(dev->sane.name, &handle)) {
1525 			dev->fd = handle;
1526 		}
1527 	}
1528 
1529 	/* needs to be recalibrated */
1530 	dev->scanning.fCalibrated = SANE_FALSE;
1531 
1532 	if( -1 != dev->fd )
1533 		usb_LampOn( dev, sw, SANE_FALSE );
1534 
1535 	if( -1 != handle ) {
1536 		dev->fd = -1;
1537 		sanei_usb_close( handle );
1538 	}
1539 }
1540 
1541 /* HEINER: replace!!! */
1542 static Plustek_Device *dev_xxx = NULL;
1543 
1544 /** ISR to switch lamp off after time has elapsed
1545  */
1546 static void
usb_LampTimerIrq(int sig)1547 usb_LampTimerIrq( int sig )
1548 {
1549 	if( NULL == dev_xxx )
1550 		return;
1551 
1552 	_VAR_NOT_USED( sig );
1553 	DBG( _DBG_INFO, "LAMP OFF!!!\n" );
1554 
1555 	usb_LampSwitch( dev_xxx, SANE_FALSE );
1556 }
1557 
1558 /** usb_StartLampTimer
1559  */
1560 static void
usb_StartLampTimer(Plustek_Device * dev)1561 usb_StartLampTimer( Plustek_Device *dev )
1562 {
1563 	sigset_t         block, pause_mask;
1564 	struct sigaction s;
1565 #ifdef HAVE_SETITIMER
1566 	struct itimerval interval;
1567 #endif
1568 	/* block SIGALRM */
1569 	sigemptyset( &block );
1570 	sigaddset  ( &block, SIGALRM );
1571 	sigprocmask( SIG_BLOCK, &block, &pause_mask );
1572 
1573 	/* setup handler */
1574 	sigemptyset( &s.sa_mask );
1575 	sigaddset  ( &s.sa_mask, SIGALRM );
1576 	s.sa_flags   = 0;
1577 	s.sa_handler = usb_LampTimerIrq;
1578 
1579 	if( sigaction( SIGALRM, &s, NULL ) < 0 )
1580 		DBG( _DBG_ERROR, "Can't setup timer-irq handler\n" );
1581 
1582 	sigprocmask( SIG_UNBLOCK, &block, &pause_mask );
1583 
1584 #ifdef HAVE_SETITIMER
1585 	/* define a one-shot timer */
1586 	interval.it_value.tv_usec    = 0;
1587 	interval.it_value.tv_sec     = dev->usbDev.dwLampOnPeriod;
1588 	interval.it_interval.tv_usec = 0;
1589 	interval.it_interval.tv_sec  = 0;
1590 
1591 	if( 0 != dev->usbDev.dwLampOnPeriod ) {
1592 		dev_xxx = dev;
1593 		setitimer( ITIMER_REAL, &interval, &dev->saveSettings );
1594 		DBG( _DBG_INFO, "Lamp-Timer started (using ITIMER)\n" );
1595 	}
1596 #else
1597 	if( 0 != dev->usbDev.dwLampOnPeriod ) {
1598 		dev_xxx = dev;
1599 		alarm( dev->usbDev.dwLampOnPeriod );
1600 		DBG( _DBG_INFO, "Lamp-Timer started (using ALARM)\n" );
1601 	}
1602 #endif
1603 }
1604 
1605 /** usb_StopLampTimer
1606  */
1607 static void
usb_StopLampTimer(Plustek_Device * dev)1608 usb_StopLampTimer( Plustek_Device *dev )
1609 {
1610 	sigset_t block, pause_mask;
1611 
1612 	/* block SIGALRM */
1613 	sigemptyset( &block );
1614 	sigaddset  ( &block, SIGALRM );
1615 	sigprocmask( SIG_BLOCK, &block, &pause_mask );
1616 
1617 	dev_xxx = NULL;
1618 
1619 #ifdef HAVE_SETITIMER
1620 	if( 0 != dev->usbDev.dwLampOnPeriod )
1621 		setitimer( ITIMER_REAL, &dev->saveSettings, NULL );
1622 #else
1623 	_VAR_NOT_USED( dev );
1624 	alarm( 0 );
1625 #endif
1626 	DBG( _DBG_INFO, "Lamp-Timer stopped\n" );
1627 }
1628 
1629 /** wait until warmup has been done
1630  */
1631 static SANE_Bool
usb_Wait4Warmup(Plustek_Device * dev)1632 usb_Wait4Warmup( Plustek_Device *dev )
1633 {
1634 	u_long         dw;
1635 	struct timeval t;
1636 
1637 	if( usb_IsCISDevice(dev)) {
1638 		DBG(_DBG_INFO,"Warmup: skipped for CIS devices\n" );
1639 		return SANE_TRUE;
1640 	}
1641 
1642 	if( dev->adj.warmup < 0 )
1643 		return SANE_TRUE;
1644 
1645 	/*
1646 	 * wait until warmup period has been elapsed
1647 	 */
1648 	gettimeofday( &t, NULL);
1649 	dw = t.tv_sec - dev->usbDev.dwTicksLampOn;
1650 	if( dw < (u_long)dev->adj.warmup )
1651 		DBG(_DBG_INFO,"Warmup: Waiting %d seconds\n", dev->adj.warmup );
1652 
1653 	do {
1654 
1655 		gettimeofday( &t, NULL);
1656 
1657 		dw = t.tv_sec - dev->usbDev.dwTicksLampOn;
1658 
1659 		if( usb_IsEscPressed()) {
1660 			return SANE_FALSE;
1661 		}
1662 
1663 	} while( dw < (u_long)dev->adj.warmup );
1664 
1665 	return SANE_TRUE;
1666 }
1667 
1668 /** function for TPA autodection (EPSON & UMAX devices)
1669  */
1670 static SANE_Bool
usb_HasTPA(Plustek_Device * dev)1671 usb_HasTPA( Plustek_Device *dev )
1672 {
1673 	static char model[] = { "3450" };
1674 	u_char val;
1675 
1676 	if( dev->usbDev.vendor == 0x04B8 ) {         /* the EPSON section */
1677 
1678 		usb_switchLampX( dev, SANE_FALSE, SANE_TRUE );
1679 		usbio_WriteReg ( dev->fd, 0x58, 0x1d );
1680 		usbio_WriteReg ( dev->fd, 0x59, 0x49 );
1681 		usbio_ReadReg  ( dev->fd, 0x02, &val );
1682 		usbio_WriteReg ( dev->fd, 0x58, dev->usbDev.HwSetting.bReg_0x58 );
1683 		usbio_WriteReg ( dev->fd, 0x59, dev->usbDev.HwSetting.bReg_0x59 );
1684 
1685 		DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val );
1686 
1687 		if( val & 0x02 ) {
1688 			DBG( _DBG_INFO, "EPSON-TPA detected\n" );
1689 			return SANE_TRUE;
1690 		} else
1691 			DBG( _DBG_INFO, "EPSON-TPA NOT detected\n" );
1692 
1693 		if( dev->adj.enableTpa ) {
1694 			DBG( _DBG_INFO, "EPSON-TPA usage forced\n" );
1695 			return SANE_TRUE;
1696 		}
1697 
1698 	} else if( dev->usbDev.vendor == 0x1606 ) { /* the UMAX section   */
1699 
1700 		if((dev->usbDev.product == 0x0050) || (dev->usbDev.product == 0x0060)) {
1701 
1702 			usbio_ReadReg  ( dev->fd, 0x02, &val );
1703 			DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val );
1704 
1705 			usbio_WriteReg ( dev->fd, 0x58, dev->usbDev.HwSetting.bReg_0x58 );
1706 			usbio_WriteReg ( dev->fd, 0x5a, dev->usbDev.HwSetting.bReg_0x5a );
1707 			usbio_WriteReg ( dev->fd, 0x5b, dev->usbDev.HwSetting.bReg_0x5b );
1708 
1709 			usbio_ReadReg  ( dev->fd, 0x02, &val );
1710 			DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val );
1711 
1712 			if( val & 0x02 ) {
1713 				DBG( _DBG_INFO, "UMAX-TPA detected\n" );
1714 				dev->usbDev.ModelStr = model;
1715 				return SANE_TRUE;
1716 			} else
1717 				DBG( _DBG_INFO, "UMAX-TPA NOT detected\n" );
1718 
1719 			if( dev->adj.enableTpa ) {
1720 				DBG( _DBG_INFO, "UMAX-TPA usage forced\n" );
1721 				dev->usbDev.ModelStr = model;
1722 				return SANE_TRUE;
1723 			}
1724 		}
1725 	}
1726 	return SANE_FALSE;
1727 }
1728 
1729 /** function for reading the button states
1730  */
1731 static SANE_Bool
usb_UpdateButtonStatus(Plustek_Scanner * s)1732 usb_UpdateButtonStatus( Plustek_Scanner *s )
1733 {
1734 	u_char          mio[3];
1735 	SANE_Byte       val, mask;
1736 	int             i, j, bc;
1737 	int             handle = -1;
1738 	SANE_Status     status;
1739 	Plustek_Device *dev  = s->hw;
1740 	DCapsDef       *caps = &dev->usbDev.Caps;
1741 
1742 	if (caps->bButtons == 0)
1743 		return SANE_FALSE;
1744 
1745 	status = sanei_access_lock( dev->sane.name, 3 );
1746 	if( SANE_STATUS_GOOD != status )
1747 		return SANE_FALSE;
1748 
1749 	if( -1 == dev->fd ) {
1750 
1751 		status = sanei_usb_open(dev->sane.name, &handle);
1752 		if( SANE_STATUS_GOOD != status ) {
1753 			sanei_access_unlock( dev->sane.name );
1754 			return SANE_FALSE;
1755 		}
1756 		dev->fd = handle;
1757 	}
1758 
1759 	/* we have to check all 6 misc I/O ports for input configuration*/
1760 	mio[0] = dev->usbDev.HwSetting.bReg_0x59;
1761 	mio[1] = dev->usbDev.HwSetting.bReg_0x5a;
1762 	mio[2] = dev->usbDev.HwSetting.bReg_0x5b;
1763 
1764 	usbio_ReadReg( dev->fd, 0x07, &val );
1765 	if( val == 0 ) {
1766 
1767 		/* first read clears the status... */
1768 		usbio_ReadReg( dev->fd, 0x02, &val );
1769 
1770 		/* Plustek and KYE/Genius use altnernative button handling */
1771 		if((dev->usbDev.vendor == 0x07B3) || (dev->usbDev.vendor == 0x0458)) {
1772 
1773 			DBG( _DBG_INFO2, "Button Value=0x%02x\n", val );
1774 
1775 			/* no button pressed so far */
1776 			for( i = 0; i < caps->bButtons; i++ )
1777 				s->val[OPT_BUTTON_0 + i].w = 0;
1778 
1779 			if (caps->bButtons == 2 || caps->bButtons == 5) {
1780 				val >>= 2;
1781 				val &= 0x07;
1782 				DBG( _DBG_INFO2, "Button Value=0x%02x (2/5)\n", val );
1783 
1784 				switch( val ) {
1785 					case 1: s->val[OPT_BUTTON_1].w = 1; break;
1786 					case 2: s->val[OPT_BUTTON_0].w = 1; break;
1787 					case 3: s->val[OPT_BUTTON_2].w = 1; break;
1788 					case 4: s->val[OPT_BUTTON_3].w = 1; break;
1789 					case 6: s->val[OPT_BUTTON_4].w = 1; break;
1790 				}
1791 			} else if (caps->bButtons == 4 ) {
1792 				val >>= 5;
1793 				val &= 0x07;
1794 				DBG( _DBG_INFO2, "Button Value=0x%02x (4)\n", val );
1795 
1796 				switch( val ) {
1797 					case 1: s->val[OPT_BUTTON_0].w = 1; break;
1798 					case 2: s->val[OPT_BUTTON_1].w = 1; break;
1799 					case 4: s->val[OPT_BUTTON_2].w = 1; break;
1800 					case 6: s->val[OPT_BUTTON_3].w = 1; break;
1801 				}
1802 			} else {
1803 				DBG( _DBG_INFO2, "Hmm, could not handle this!\n" );
1804 			}
1805 
1806 		} else {
1807 
1808 			/* the generic section... */
1809 			val >>= 2;
1810 			bc    = 0;
1811 
1812 			/* only use the "valid" ports, that reflect a button */
1813 			if( _WAF_MISC_IO_BUTTONS & caps->workaroundFlag ) {
1814 				if((caps->misc_io & _PORT0) == 0)
1815 					mio[0] = 0xff;
1816 				if((caps->misc_io & _PORT1) == 0)
1817 					mio[1] = 0xff;
1818 				if((caps->misc_io & _PORT2) == 0)
1819 					mio[2] = 0xff;
1820 			}
1821 
1822 			for( i = 0; i < 3; i++ ) {
1823 
1824 				DBG( _DBG_INFO2, "Checking MISC IO[%u]=0x%02x\n", i, mio[i] );
1825 				mask = 0x01;
1826 
1827 				for( j = 0; j < 2; j++ ) {
1828 
1829 					if((mio[i] & mask) == 0) {
1830 						DBG( _DBG_INFO2, "* port %u configured as input,"
1831 						     " status: %s (%u)\n", (i*2)+j+1,
1832 							 ((val & 1)?"PRESSED":"RELEASED"), (OPT_BUTTON_0 + bc));
1833 						s->val[OPT_BUTTON_0 + bc].w = val & 1;
1834 						bc++;
1835 					}
1836 					val  >>= 1;
1837 					mask <<= 4;
1838 				}
1839 			}
1840 		}
1841 	} else {
1842 		DBG( _DBG_INFO2, "Scanner NOT idle: 0x%02x\n", val );
1843 	}
1844 
1845 	if( -1 != handle ) {
1846 		dev->fd = -1;
1847 		sanei_usb_close( handle );
1848 	}
1849 
1850 	sanei_access_unlock( dev->sane.name );
1851 	return SANE_TRUE;
1852 }
1853 
1854 /* END PLUSTEK-USBHW.C ......................................................*/
1855