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, ®s[0x48], 2, SANE_TRUE));
591 _UIO(sanei_lm983x_write(dev->fd, 0x4A, ®s[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, ®s[0x48], 4, SANE_TRUE));
806 _UIO(sanei_lm983x_write(dev->fd, 0x56, ®s[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, ®s[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( ®s[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 ®s[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, ®, &msk );
1075
1076 if( 0 == reg ) {
1077 usbio_ReadReg( dev->fd, 0x29, ® );
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), ®, &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, ®s[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,®s[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), ®, &msk );
1149 else
1150 usb_GetLampRegAndMask( sc->misc_io, ®, &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( ®s[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 ®s[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( ®s[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 ®s[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, ®s[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, ®s[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