1 /*.............................................................................
2 * Project : SANE library for Plustek flatbed scanners.
3 *.............................................................................
4 */
5
6 /** @file plustek-usbshading.c
7 * @brief Calibration routines.
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 - minor fixes
15 * - added workaround stuff for EPSON1250
16 * - 0.42 - added workaround stuff for UMAX 3400
17 * - 0.43 - added call to usb_switchLamp before reading dark data
18 * - 0.44 - added more debug output and bypass calibration
19 * - added dump of shading data
20 * - 0.45 - added coarse calibration for CIS devices
21 * - added _WAF_SKIP_FINE to skip the results of fine calibration
22 * - CanoScan fixes and fine-tuning
23 * - 0.46 - CanoScan will now be calibrated by code in plustek-usbcal.c
24 * - added functions to save and restore calibration data from a file
25 * - fixed some TPA issues
26 * - 0.47 - made calibration work on big-endian machines
27 * - 0.48 - added more debug output
28 * - added usb_AutoWarmup()
29 * - 0.49 - a_bRegs is now part of the device structure
30 * - using now PhyDpi.y as selector for the motor MCLK range
31 * - 0.50 - readded kCIS670 to add 5% extra to LiDE20 fine calibration
32 * - fixed line statistics and added data output
33 * - 0.51 - added fine calibration cache
34 * - 0.52 - added get_ptrs to let various sensororders work
35 * correctly
36 * - fixed warning condition
37 * .
38 * <hr>
39 * This file is part of the SANE package.
40 *
41 * This program is free software; you can redistribute it and/or
42 * modify it under the terms of the GNU General Public License as
43 * published by the Free Software Foundation; either version 2 of the
44 * License, or (at your option) any later version.
45 *
46 * This program is distributed in the hope that it will be useful, but
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
49 * General Public License for more details.
50 *
51 * You should have received a copy of the GNU General Public License
52 * along with this program. If not, see <https://www.gnu.org/licenses/>.
53 *
54 * As a special exception, the authors of SANE give permission for
55 * additional uses of the libraries contained in this release of SANE.
56 *
57 * The exception is that, if you link a SANE library with other files
58 * to produce an executable, this does not by itself cause the
59 * resulting executable to be covered by the GNU General Public
60 * License. Your use of that executable is in no way restricted on
61 * account of linking the SANE library code into it.
62 *
63 * This exception does not, however, invalidate any other reasons why
64 * the executable file might be covered by the GNU General Public
65 * License.
66 *
67 * If you submit changes to SANE to the maintainers to be included in
68 * a subsequent release, you agree by submitting the changes that
69 * those changes may be distributed with this exception intact.
70 *
71 * If you write modifications of your own for SANE, it is your choice
72 * whether to permit this exception to apply to your modifications.
73 * If you do not wish that, delete this exception notice.
74 * <hr>
75 */
76
77 /************** global stuff - I hate it, but we need it... ******************/
78
79 #define _MAX_AUTO_WARMUP 60 /**< number of loops */
80 #define _AUTO_SLEEP 1 /**< time to sleep, when lamp is not stable */
81 #define _AUTO_THRESH 60 /**< threshold for stop waiting (normal lamp) */
82 #define _AUTO_TPA_THRESH 40 /**< threshold for stop waiting (TPA lamp) */
83
84 #define _MAX_GAIN_LOOPS 10 /**< max number of loops for coarse calibration */
85 #define _TLOOPS 3 /**< test loops for transfer rate measurement */
86
87 #define SWAP_COARSE
88 #define SWAP_FINE
89
90 /************************** static variables *********************************/
91
92 static RGBUShortDef Gain_Hilight;
93 static RGBUShortDef Gain_NegHilight;
94 static RGBByteDef Gain_Reg;
95
96 static u_long m_dwPixels;
97 static ScanParam m_ScanParam;
98 static u_long m_dwIdealGain;
99
100 static double dMCLK, dExpect, dMax;
101 static double dMCLK_ADF;
102
103 /** do some statistics...
104 */
usb_line_statistics(char * cmt,u_short * buf,u_long dim_x,SANE_Bool color)105 static void usb_line_statistics( char *cmt, u_short* buf,
106 u_long dim_x, SANE_Bool color )
107 {
108 char fn[50];
109 int i, channel;
110 u_long dw, imad, imid, alld, cld, cud;
111 u_short mid, mad, aved, lbd, ubd, tmp;
112 MonoWordDef *pvd, *pvd2;
113 FILE *fp;
114 SANE_Bool swap = usb_HostSwap();
115
116 pvd = pvd2 = (MonoWordDef*)buf;
117
118 if( color )
119 channel = 3;
120 else
121 channel = 1;
122
123 for( i = 0; i < channel; i++ ) {
124
125 mid = 0xFFFF;
126 mad = 0;
127 imid = 0;
128 imad = 0;
129 alld = 0;
130
131 fp = NULL;
132 if( DBG_LEVEL >= _DBG_DCALDATA ) {
133 sprintf( fn, "%scal%u.dat", cmt, i );
134 fp = fopen( fn, "w+b" );
135 if( fp == NULL )
136 DBG( _DBG_ERROR, "Could not open %s\n", fn );
137 }
138
139 /* do the standard min/max stuff */
140 for( dw = 0; dw < dim_x; pvd++, dw++ ) {
141
142 if( swap )
143 tmp = _LOBYTE(pvd->Mono) * 256 + _HIBYTE(pvd->Mono);
144 else
145 tmp = pvd->Mono;
146
147 if( tmp > mad ) {
148 mad = tmp;
149 imad = dw;
150 }
151
152 if( tmp < mid ) {
153 mid = tmp;
154 imid = dw;
155 }
156
157 if( fp )
158 fprintf(fp, "%u\n", tmp );
159
160 alld += tmp;
161 }
162
163 if( fp )
164 fclose(fp);
165
166 /* calculate average and 5% limit */
167 aved = (u_short)(alld/dim_x);
168 lbd = aved - 0.05*aved;
169 ubd = aved + 0.05*aved;
170 cld = 0;
171 cud = 0;
172
173 /* find the number of values beyond the 5% limits */
174 for( dw = 0; dw < dim_x; pvd2++, dw++ ) {
175
176 if( swap )
177 tmp = _LOBYTE(pvd2->Mono) * 256 + _HIBYTE(pvd2->Mono);
178 else
179 tmp = pvd2->Mono;
180
181 if( tmp > ubd ) cud++;
182 if( tmp < lbd ) cld++;
183 }
184
185 DBG( _DBG_INFO2, "Color[%u] (%s): %lu all "
186 "min=%u(%lu) max=%u(%lu) ave=%u\n",
187 i, cmt, dim_x, mid, imid, mad, imad, aved);
188 DBG( _DBG_INFO2, "5%%: low@%u (count=%lu), upper@%u (count=%lu)\n",
189 lbd, cld, ubd, cud);
190 }
191 }
192
193 /**
194 */
195 static void
usb_PrepareFineCal(Plustek_Device * dev,ScanParam * tmp_sp,u_short cal_dpi)196 usb_PrepareFineCal( Plustek_Device *dev, ScanParam *tmp_sp, u_short cal_dpi )
197 {
198 ScanParam *sp = &dev->scanning.sParam;
199 DCapsDef *scaps = &dev->usbDev.Caps;
200
201 *tmp_sp = *sp;
202
203 if( dev->adj.cacheCalData ) {
204
205 DBG( _DBG_INFO2, "* Cal-cache active, tweaking scanparams"
206 " - DPI=%u!\n", cal_dpi );
207
208 tmp_sp->UserDpi.x = usb_SetAsicDpiX(dev, sp->UserDpi.x );
209 if( cal_dpi != 0 )
210 tmp_sp->UserDpi.x = cal_dpi;
211
212 tmp_sp->PhyDpi = scaps->OpticDpi;
213 tmp_sp->Origin.x = 0;
214 tmp_sp->Size.dwPixels = scaps->Normal.Size.x *
215 usb_SetAsicDpiX(dev, tmp_sp->UserDpi.x)/ 300UL;
216 }
217
218 #if 0
219 if( tmp_sp->PhyDpi.x > 75)
220 tmp_sp->Size.dwLines = 64;
221 else
222 #endif
223 tmp_sp->Size.dwLines = 32;
224
225 tmp_sp->Origin.y = 0;
226 tmp_sp->bBitDepth = 16;
227
228 tmp_sp->UserDpi.y = scaps->OpticDpi.y;
229 tmp_sp->Size.dwBytes = tmp_sp->Size.dwPixels * 2 * tmp_sp->bChannels;
230
231 if( usb_IsCISDevice(dev) && (tmp_sp->bDataType == SCANDATATYPE_Color)) {
232 tmp_sp->Size.dwBytes *= 3;
233 }
234
235 tmp_sp->dMCLK = dMCLK;
236 }
237
238 /**
239 */
usb_GetMCLK(Plustek_Device * dev,ScanParam * param)240 static double usb_GetMCLK( Plustek_Device *dev, ScanParam *param )
241 {
242 int idx, i;
243 double mclk;
244 ClkMotorDef *clk;
245 HWDef *hw = &dev->usbDev.HwSetting;
246
247 clk = usb_GetMotorSet( hw->motorModel );
248 idx = 0;
249 for( i = 0; i < _MAX_CLK; i++ ) {
250 if( param->PhyDpi.y <= dpi_ranges[i] )
251 break;
252 idx++;
253 }
254 if( idx >= _MAX_CLK )
255 idx = _MAX_CLK - 1;
256
257 if( param->bDataType != SCANDATATYPE_Color ) {
258
259 if( param->bBitDepth > 8 )
260 mclk = clk->gray_mclk_16[idx];
261 else
262 mclk = clk->gray_mclk_8[idx];
263 } else {
264 if( param->bBitDepth > 8 )
265 mclk = clk->color_mclk_16[idx];
266 else
267 mclk = clk->color_mclk_8[idx];
268 }
269
270 DBG( _DBG_INFO, "GETMCLK[%u/%u], using entry %u: %.3f, %u\n",
271 hw->motorModel, param->bDataType, idx, mclk, param->PhyDpi.x );
272 return mclk;
273 }
274
275 /** usb_SetMCLK
276 * get the MCLK out of our table
277 */
usb_SetMCLK(Plustek_Device * dev,ScanParam * param)278 static void usb_SetMCLK( Plustek_Device *dev, ScanParam *param )
279 {
280 HWDef *hw = &dev->usbDev.HwSetting;
281
282 dMCLK = usb_GetMCLK( dev, param );
283 param->dMCLK = dMCLK;
284
285 DBG( _DBG_INFO, "SETMCLK[%u/%u]: %.3f\n",
286 hw->motorModel, param->bDataType, dMCLK );
287 }
288
289 /** usb_SetDarkShading
290 * download the dark shading data to Merlins' DRAM
291 */
usb_SetDarkShading(Plustek_Device * dev,u_char channel,void * coeff_buffer,u_short wCount)292 static SANE_Bool usb_SetDarkShading( Plustek_Device *dev, u_char channel,
293 void *coeff_buffer, u_short wCount )
294 {
295 int res;
296 u_char *regs = dev->usbDev.a_bRegs;
297
298 regs[0x03] = 0;
299 if( channel == CHANNEL_green )
300 regs[0x03] |= 4;
301 else
302 if( channel == CHANNEL_blue )
303 regs[0x03] |= 8;
304
305 if( usbio_WriteReg( dev->fd, 0x03, regs[0x03] )) {
306
307 /* Dataport address is always 0 for setting offset coefficient */
308 regs[0x04] = 0;
309 regs[0x05] = 0;
310
311 res = sanei_lm983x_write( dev->fd, 0x04, ®s[0x04], 2, SANE_TRUE );
312
313 /* Download offset coefficients */
314 if( SANE_STATUS_GOOD == res ) {
315
316 res = sanei_lm983x_write( dev->fd, 0x06,
317 (u_char*)coeff_buffer, wCount, SANE_FALSE );
318 if( SANE_STATUS_GOOD == res )
319 return SANE_TRUE;
320 }
321 }
322
323 DBG( _DBG_ERROR, "usb_SetDarkShading() failed\n" );
324 return SANE_FALSE;
325 }
326
327 /** usb_SetWhiteShading
328 * download the white shading data to Merlins' DRAM
329 */
usb_SetWhiteShading(Plustek_Device * dev,u_char channel,void * data_buffer,u_short wCount)330 static SANE_Bool usb_SetWhiteShading( Plustek_Device *dev, u_char channel,
331 void *data_buffer, u_short wCount )
332 {
333 int res;
334 u_char *regs = dev->usbDev.a_bRegs;
335
336 regs[0x03] = 1;
337 if (channel == CHANNEL_green)
338 regs [0x03] |= 4;
339 else
340 if (channel == CHANNEL_blue)
341 regs[0x03] |= 8;
342
343 if( usbio_WriteReg( dev->fd, 0x03, regs[0x03] )) {
344
345 /* Dataport address is always 0 for setting offset coefficient */
346 regs[0x04] = 0;
347 regs[0x05] = 0;
348
349 res = sanei_lm983x_write( dev->fd, 0x04, ®s[0x04], 2, SANE_TRUE );
350
351 /* Download offset coefficients */
352 if( SANE_STATUS_GOOD == res ) {
353 res = sanei_lm983x_write(dev->fd, 0x06,
354 (u_char*)data_buffer, wCount, SANE_FALSE );
355 if( SANE_STATUS_GOOD == res )
356 return SANE_TRUE;
357 }
358 }
359
360 DBG( _DBG_ERROR, "usb_SetWhiteShading() failed\n" );
361 return SANE_FALSE;
362 }
363
364 /** usb_GetSWOffsetGain4TPA
365 * preset the offset and gain parameter for fine calibration for
366 * TPA/ADF scanning
367 */
usb_GetSWOffsetGain4TPA(Plustek_Device * dev)368 static void usb_GetSWOffsetGain4TPA( Plustek_Device *dev )
369 {
370 ScanParam *param = &dev->scanning.sParam;
371 DCapsDef *sCaps = &dev->usbDev.Caps;
372
373 switch( sCaps->bCCD ) {
374 case kEPSON:
375 DBG( _DBG_INFO2, "kEPSON TPA adjustments\n" );
376 param->swGain[0] = 1000;
377 param->swGain[1] = 1000;
378 param->swGain[2] = 1000;
379 break;
380 }
381 }
382
383 /** usb_GetSWOffsetGain
384 * preset the offset and gain parameter for fine calibration for normal
385 * scanning
386 */
usb_GetSWOffsetGain(Plustek_Device * dev)387 static void usb_GetSWOffsetGain( Plustek_Device *dev )
388 {
389 ScanParam *param = &dev->scanning.sParam;
390 DCapsDef *sCaps = &dev->usbDev.Caps;
391 HWDef *hw = &dev->usbDev.HwSetting;
392
393 param->swOffset[0] = 0;
394 param->swOffset[1] = 0;
395 param->swOffset[2] = 0;
396
397 param->swGain[0] = 1000;
398 param->swGain[1] = 1000;
399 param->swGain[2] = 1000;
400
401 if( param->bSource != SOURCE_Reflection ) {
402 usb_GetSWOffsetGain4TPA( dev );
403 return;
404 }
405
406 /* only valid for normal scanning... */
407 switch( sCaps->bCCD ) {
408
409 case kEPSON:
410 DBG( _DBG_INFO2, "kEPSON adjustments\n" );
411 #if 0
412 param->swGain[0] = 800;
413 param->swGain[1] = 800;
414 param->swGain[2] = 800;
415 #endif
416 break;
417
418 case kNECSLIM:
419 DBG( _DBG_INFO2, "kNECSLIM adjustments\n" );
420 if( param->PhyDpi.x <= 150 ) {
421 param->swOffset[0] = 600;
422 param->swOffset[1] = 500;
423 param->swOffset[2] = 300;
424 param->swGain[0] = 960;
425 param->swGain[1] = 970;
426 param->swGain[2] = 1000;
427 } else if (param->PhyDpi.x <= 300) {
428 param->swOffset[0] = 700;
429 param->swOffset[1] = 600;
430 param->swOffset[2] = 400;
431 param->swGain[0] = 967;
432 param->swGain[1] = 980;
433 param->swGain[2] = 1000;
434 } else {
435 param->swOffset[0] = 900;
436 param->swOffset[1] = 850;
437 param->swOffset[2] = 620;
438 param->swGain[0] = 965;
439 param->swGain[1] = 980;
440 param->swGain[2] = 1000;
441 }
442 break;
443
444 case kNEC8861:
445 DBG( _DBG_INFO2, "kNEC8861 adjustments\n" );
446 break;
447
448 case kCIS670:
449 DBG( _DBG_INFO2, "kCIS670 adjustments\n" );
450 if(param->bDataType == SCANDATATYPE_Color) {
451
452 param->swGain[0] =
453 param->swGain[1] =
454 param->swGain[2] = 952;
455
456 param->swOffset[0] =
457 param->swOffset[1] =
458 param->swOffset[2] = 1000;
459 }
460 break;
461 #if 0
462 case kCIS650:
463 case kCIS1220:
464
465 DBG( _DBG_INFO2, "kCIS adjustments\n" );
466 if(param->bDataType == SCANDATATYPE_Color) {
467
468 param->swGain[0] =
469 param->swGain[1] =
470 param->swGain[2] = 952;
471
472 param->swOffset[0] =
473 param->swOffset[1] =
474 param->swOffset[2] = 1000;
475 }
476 break;
477 case kCIS1240:
478 DBG( _DBG_INFO2, "kCIS1240 adjustments\n" );
479 if(param->bDataType == SCANDATATYPE_Color) {
480
481 param->swGain[0] = 950;
482 param->swGain[1] = 950;
483 param->swGain[2] = 900;
484
485 param->swOffset[0] =
486 param->swOffset[1] =
487 param->swOffset[2] = 0; /*1000;*/
488 }
489 break;
490 #endif
491
492 case kNEC3799:
493 DBG( _DBG_INFO2, "kNEC3799 adjustments\n" );
494 if( sCaps->bPCB == 2 ) {
495 if( param->PhyDpi.x <= 150 ) {
496 param->swOffset[0] = 600;
497 param->swOffset[1] = 500;
498 param->swOffset[2] = 300;
499 param->swGain[0] = 960;
500 param->swGain[1] = 970;
501 param->swGain[2] = 1000;
502 } else if (param->PhyDpi.x <= 300) {
503 param->swOffset[0] = 700;
504 param->swOffset[1] = 600;
505 param->swOffset[2] = 400;
506 param->swGain[0] = 967;
507 param->swGain[1] = 980;
508 param->swGain[2] = 1000;
509 } else {
510 param->swOffset[0] = 900;
511 param->swOffset[1] = 850;
512 param->swOffset[2] = 620;
513 param->swGain[0] = 965;
514 param->swGain[1] = 980;
515 param->swGain[2] = 1000;
516 }
517 } else if( hw->motorModel == MODEL_KaoHsiung ) {
518 param->swOffset[0] = 1950;
519 param->swOffset[1] = 1700;
520 param->swOffset[2] = 1250;
521 param->swGain[0] = 955;
522 param->swGain[1] = 950;
523 param->swGain[2] = 1000;
524 } else { /* MODEL_Hualien */
525 if( param->PhyDpi.x <= 300 ) {
526 if( param->bBitDepth > 8 ) {
527 param->swOffset[0] = 0;
528 param->swOffset[1] = 0;
529 param->swOffset[2] = -300;
530 param->swGain[0] = 970;
531 param->swGain[1] = 985;
532 param->swGain[2] = 1050;
533 } else {
534 param->swOffset[0] = -485;
535 param->swOffset[1] = -375;
536 param->swOffset[2] = -628;
537 param->swGain[0] = 970;
538 param->swGain[1] = 980;
539 param->swGain[2] = 1050;
540 }
541 } else {
542 if( param->bBitDepth > 8 ) {
543 param->swOffset[0] = 1150;
544 param->swOffset[1] = 1000;
545 param->swOffset[2] = 700;
546 param->swGain[0] = 990;
547 param->swGain[1] = 1000;
548 param->swGain[2] = 1050;
549 } else {
550 param->swOffset[0] = -30;
551 param->swOffset[1] = 0;
552 param->swOffset[2] = -250;
553 param->swGain[0] = 985;
554 param->swGain[1] = 995;
555 param->swGain[2] = 1050;
556 }
557 }
558 }
559 break;
560
561 case kSONY548:
562 DBG( _DBG_INFO2, "kSONY548 adjustments\n" );
563 if(param->bDataType == SCANDATATYPE_Color)
564 {
565 if(param->PhyDpi.x <= 75)
566 {
567 param->swOffset[0] = 650;
568 param->swOffset[1] = 850;
569 param->swOffset[2] = 500;
570 param->swGain[0] = 980;
571 param->swGain[1] = 1004;
572 param->swGain[2] = 1036;
573 }
574 else if(param->PhyDpi.x <= 300)
575 {
576 param->swOffset[0] = 700;
577 param->swOffset[1] = 900;
578 param->swOffset[2] = 550;
579 param->swGain[0] = 970;
580 param->swGain[1] = 995;
581 param->swGain[2] = 1020;
582 }
583 else if(param->PhyDpi.x <= 400)
584 {
585 param->swOffset[0] = 770;
586 param->swOffset[1] = 1010;
587 param->swOffset[2] = 600;
588 param->swGain[0] = 970;
589 param->swGain[1] = 993;
590 param->swGain[2] = 1023;
591 }
592 else
593 {
594 param->swOffset[0] = 380;
595 param->swOffset[1] = 920;
596 param->swOffset[2] = 450;
597 param->swGain[0] = 957;
598 param->swGain[1] = 980;
599 param->swGain[2] = 1008;
600 }
601 }
602 else
603 {
604 if(param->PhyDpi.x <= 75)
605 {
606 param->swOffset[1] = 1250;
607 param->swGain[1] = 950;
608 }
609 else if(param->PhyDpi.x <= 300)
610 {
611 param->swOffset[1] = 1250;
612 param->swGain[1] = 950;
613 }
614 else if(param->PhyDpi.x <= 400)
615 {
616 param->swOffset[1] = 1250;
617 param->swGain[1] = 950;
618 }
619 else
620 {
621 param->swOffset[1] = 1250;
622 param->swGain[1] = 950;
623 }
624 }
625 break;
626
627 case kNEC3778:
628 DBG( _DBG_INFO2, "kNEC3778 adjustments\n" );
629 if((_LM9831 == hw->chip) && (param->PhyDpi.x <= 300))
630 {
631 param->swOffset[0] = 0;
632 param->swOffset[1] = 0;
633 param->swOffset[2] = 0;
634 param->swGain[0] = 900;
635 param->swGain[1] = 920;
636 param->swGain[2] = 980;
637 }
638 else if( hw->motorModel == MODEL_HuaLien && param->PhyDpi.x > 800)
639 {
640 param->swOffset[0] = 0;
641 param->swOffset[1] = 0;
642 param->swOffset[2] = -200;
643 param->swGain[0] = 980;
644 param->swGain[1] = 930;
645 param->swGain[2] = 1080;
646 }
647 else
648 {
649 param->swOffset[0] = -304;
650 param->swOffset[1] = -304;
651 param->swOffset[2] = -304;
652 param->swGain[0] = 910;
653 param->swGain[1] = 920;
654 param->swGain[2] = 975;
655 }
656
657 if(param->bDataType == SCANDATATYPE_BW && param->PhyDpi.x <= 300)
658 {
659 param->swOffset[1] = 1000;
660 param->swGain[1] = 1000;
661 }
662 break;
663 }
664 }
665
666 /** according to the pixel values,
667 */
usb_GetNewGain(Plustek_Device * dev,u_short wMax,int channel)668 static u_char usb_GetNewGain( Plustek_Device *dev, u_short wMax, int channel )
669 {
670 double dRatio, dAmp;
671 u_long dwInc, dwDec;
672 u_char bGain;
673
674 if( !wMax )
675 wMax = 1;
676
677 dAmp = 0.93 + 0.067 * dev->usbDev.a_bRegs[0x3b+channel];
678
679 if((m_dwIdealGain / (wMax / dAmp)) < 3) {
680
681 dRatio = ((double) m_dwIdealGain * dAmp / wMax - 0.93) / 0.067;
682 if(ceil(dRatio) > 0x3f)
683 return 0x3f;
684
685 dwInc = (u_long)((0.93 + ceil (dRatio) * 0.067) * wMax / dAmp);
686 dwDec = (u_long)((0.93 + floor (dRatio) * 0.067) * wMax / dAmp);
687 if((dwInc >= 0xff00) ||
688 (labs (dwInc - m_dwIdealGain) > labs(dwDec - m_dwIdealGain))) {
689 bGain = (u_char)floor(dRatio);
690 } else {
691 bGain = (u_char)ceil(dRatio);
692 }
693
694 } else {
695
696 dRatio = m_dwIdealGain / (wMax / dAmp);
697 dAmp = floor((dRatio / 3 - 0.93)/0.067);
698
699 if( dAmp > 31 )
700 dAmp = 31;
701
702 bGain = (u_char)dAmp + 32;
703 }
704
705 if( bGain > 0x3f ) {
706 DBG( _DBG_INFO, "* GAIN Overflow!!!\n" );
707 bGain = 0x3f;
708 }
709 return bGain;
710 }
711
712 /** limit and set register given by address
713 * @param gain -
714 * @param reg -
715 */
setAdjGain(int gain,u_char * reg)716 static void setAdjGain( int gain, u_char *reg )
717 {
718 if( gain >= 0 ) {
719
720 if( gain > 0x3f )
721 *reg = 0x3f;
722 else
723 *reg = gain;
724 }
725 }
726
727 /**
728 * @param channel - 0 = red, 1 = green, 2 = blue
729 * @param max -
730 * @param ideal -
731 * @param l_on -
732 * @param l_off -
733 * @return
734 */
adjLampSetting(Plustek_Device * dev,int channel,u_long max,u_long ideal,u_short l_on,u_short * l_off)735 static SANE_Bool adjLampSetting( Plustek_Device *dev, int channel, u_long max,
736 u_long ideal, u_short l_on, u_short *l_off )
737 {
738 SANE_Bool adj = SANE_FALSE;
739 u_long lamp_on;
740
741 /* so if the image was too bright, we dim the lamps by 3% */
742 if( max > ideal ) {
743
744 lamp_on = (*l_off) - l_on;
745 lamp_on = (lamp_on * 97)/100;
746 *l_off = l_on + lamp_on;
747 DBG( _DBG_INFO2,
748 "lamp(%u) adjust (-3%%): %i %i\n", channel, l_on, *l_off );
749 adj = SANE_TRUE;
750 }
751
752 /* if the image was too dull, increase lamp by 1% */
753 if( dev->usbDev.a_bRegs[0x3b + channel] == 63 ) {
754
755 lamp_on = (*l_off) - l_on;
756 lamp_on += (lamp_on/100);
757 *l_off = l_on + lamp_on;
758 DBG( _DBG_INFO2,
759 "lamp(%u) adjust (+1%%): %i %i\n", channel, l_on, *l_off );
760 adj = SANE_TRUE;
761 }
762
763 return adj;
764 }
765
766 /** usb_AdjustGain
767 * function to perform the "coarse calibration step" part 1.
768 * We scan reference image pixels to determine the optimum coarse gain settings
769 * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
770 * applied at the line rate during normal scanning.
771 * The scanned line should contain a white strip with some black at the
772 * beginning. The function searches for the maximum value which corresponds to
773 * the maximum white value.
774 * Affects register 0x3b, 0x3c and 0x3d
775 *
776 */
usb_AdjustGain(Plustek_Device * dev,int fNegative)777 static SANE_Bool usb_AdjustGain( Plustek_Device *dev, int fNegative )
778 {
779 char tmp[40];
780 int i;
781 double min_mclk;
782 ScanDef *scanning = &dev->scanning;
783 DCapsDef *scaps = &dev->usbDev.Caps;
784 HWDef *hw = &dev->usbDev.HwSetting;
785 u_long *scanbuf = scanning->pScanBuffer;
786 u_char *regs = dev->usbDev.a_bRegs;
787 u_long dw, start, end, len;
788 SANE_Bool fRepeatITA = SANE_TRUE;
789
790 if( usb_IsEscPressed())
791 return SANE_FALSE;
792
793 bMaxITA = 0xff;
794
795 DBG( _DBG_INFO, "#########################\n" );
796 DBG( _DBG_INFO, "usb_AdjustGain()\n" );
797 if((dev->adj.rgain != -1) &&
798 (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
799 setAdjGain( dev->adj.rgain, ®s[0x3b] );
800 setAdjGain( dev->adj.ggain, ®s[0x3c] );
801 setAdjGain( dev->adj.bgain, ®s[0x3d] );
802 DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
803 return SANE_TRUE;
804 }
805
806 min_mclk = usb_GetMCLK( dev, &scanning->sParam );
807
808 /* define the strip to scan for coarse calibration */
809 m_ScanParam.Size.dwLines = 1;
810 m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
811 scaps->OpticDpi.x / 300UL;
812 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
813 2 * m_ScanParam.bChannels;
814
815 if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color)
816 m_ScanParam.Size.dwBytes *= 3;
817
818 m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
819 300UL / scaps->OpticDpi.x);
820 m_ScanParam.bCalibration = PARAM_Gain;
821
822 start = 0;
823 len = m_ScanParam.Size.dwPixels;
824
825 if( scanning->sParam.bSource == SOURCE_Transparency ) {
826 start = scaps->Positive.DataOrigin.x * scaps->OpticDpi.x / 300UL;
827 len = scaps->Positive.Size.x * scaps->OpticDpi.x / 300UL;
828 }
829 else if( scanning->sParam.bSource == SOURCE_Negative ) {
830 start = scaps->Negative.DataOrigin.x * scaps->OpticDpi.x / 300UL;
831 len = scaps->Negative.Size.x * scaps->OpticDpi.x / 300UL;
832 }
833 end = start + len;
834
835 start = ((u_long)dev->usbDev.pSource->DataOrigin.x*scaps->OpticDpi.x/300UL);
836 len = ((u_long)dev->usbDev.pSource->Size.x * scaps->OpticDpi.x / 300UL);
837
838 DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" );
839 DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
840 DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
841 DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
842 DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
843 DBG( _DBG_INFO2, "Start = %lu\n", start );
844 DBG( _DBG_INFO2, "Len = %lu\n", len );
845 DBG( _DBG_INFO2, "End = %lu\n", end );
846 DBG( _DBG_INFO, "MIN MCLK = %.2f\n", min_mclk );
847
848 i = 0;
849 TOGAIN:
850 m_ScanParam.dMCLK = dMCLK;
851
852 if( !usb_SetScanParameters( dev, &m_ScanParam ))
853 return SANE_FALSE;
854
855 if( !usb_ScanBegin( dev, SANE_FALSE) ||
856 !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
857 !usb_ScanEnd( dev )) {
858 DBG( _DBG_ERROR, "usb_AdjustGain() failed\n" );
859 return SANE_FALSE;
860 }
861
862 DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
863 DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
864
865 if( end > m_ScanParam.Size.dwPhyPixels )
866 end = m_ScanParam.Size.dwPhyPixels;
867
868 sprintf( tmp, "coarse-gain-%u.raw", i++ );
869
870 dumpPicInit(&m_ScanParam, tmp);
871 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
872
873 #ifdef SWAP_COARSE
874 if(usb_HostSwap())
875 #endif
876 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
877
878 if( fNegative ) {
879
880 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
881
882 RGBULongDef rgb, rgbSum;
883 u_long dwLoop = (len - start) / 20 * 20;
884 u_long dw10, dwGray, dwGrayMax;
885
886 rgb.Red = rgb.Green = rgb.Blue = dwGrayMax = 0;
887
888 for( dw = start; dwLoop; dwLoop-- ) {
889
890 rgbSum.Red = rgbSum.Green = rgbSum.Blue = 0;
891 for( dw10 = 20; dw10--; dw++ ) {
892 rgbSum.Red += (u_long)(((RGBULongDef*)scanbuf)[dw].Red);
893 rgbSum.Green += (u_long)(((RGBULongDef*)scanbuf)[dw].Green);
894 rgbSum.Blue += (u_long)(((RGBULongDef*)scanbuf)[dw].Blue);
895 }
896
897 /* do some weighting of the color planes for negatives */
898 dwGray = (rgbSum.Red * 30UL + rgbSum.Green * 59UL + rgbSum.Blue * 11UL) / 100UL;
899
900 if( fNegative == 1 || rgbSum.Red > rgbSum.Green) {
901 if( dwGray > dwGrayMax ) {
902 dwGrayMax = dwGray;
903 rgb.Red = rgbSum.Red;
904 rgb.Green = rgbSum.Green;
905 rgb.Blue = rgbSum.Blue;
906 }
907 }
908 }
909
910 Gain_Hilight.Red = (u_short)(rgb.Red / 20UL);
911 Gain_Hilight.Green = (u_short)(rgb.Green / 20UL);
912 Gain_Hilight.Blue = (u_short)(rgb.Blue / 20UL);
913 DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
914 Gain_Hilight.Red, Gain_Hilight.Red, Gain_Hilight.Green,
915 Gain_Hilight.Green, Gain_Hilight.Blue, Gain_Hilight.Blue );
916
917 regs[0x3b] = usb_GetNewGain(dev,Gain_Hilight.Red, 0 );
918 regs[0x3c] = usb_GetNewGain(dev,Gain_Hilight.Green, 1 );
919 regs[0x3d] = usb_GetNewGain(dev,Gain_Hilight.Blue, 2 );
920
921 } else {
922
923 u_long dwMax = 0, dwSum;
924 u_long dwLoop = (len - start) / 20 * 20;
925 u_long dw10;
926
927 for( dw = start; dwLoop; dwLoop-- ) {
928
929 dwSum = 0;
930 for( dw10 = 20; dw10--; dw++ )
931 dwSum += (u_long)((u_short*)scanbuf)[dw];
932
933 if((fNegative == 1) || (dwSum < 0x6000 * 20)) {
934 if( dwMax < dwSum )
935 dwMax = dwSum;
936 }
937 }
938 Gain_Hilight.Red = Gain_Hilight.Green =
939 Gain_Hilight.Blue = (u_short)(dwMax / 20UL);
940
941 Gain_Reg.Red = Gain_Reg.Green =
942 Gain_Reg.Blue = regs[0x3b] =
943 regs[0x3c] = regs[0x3d] = usb_GetNewGain(dev,Gain_Hilight.Green,1);
944 }
945 } else {
946
947 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
948
949 RGBUShortDef max_rgb, min_rgb, tmp_rgb;
950 u_long dwR, dwG, dwB;
951 u_long dwDiv = 10;
952 u_long dwLoop1 = (len - start) / dwDiv, dwLoop2;
953
954 max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0;
955 min_rgb.Red = min_rgb.Green = min_rgb.Blue = 0xffff;
956
957 /* find out the max pixel value for R, G, B */
958 for( dw = start; dwLoop1; dwLoop1-- ) {
959
960 /* do some averaging... */
961 for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0;
962 dwLoop2; dwLoop2--, dw++) {
963 if( usb_IsCISDevice(dev)) {
964 dwR += ((u_short*)scanbuf)[dw];
965 dwG += ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
966 dwB += ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
967 } else {
968 dwR += ((RGBUShortDef*)scanbuf)[dw].Red;
969 dwG += ((RGBUShortDef*)scanbuf)[dw].Green;
970 dwB += ((RGBUShortDef*)scanbuf)[dw].Blue;
971 }
972 }
973 dwR = dwR / dwDiv;
974 dwG = dwG / dwDiv;
975 dwB = dwB / dwDiv;
976
977 if(max_rgb.Red < dwR)
978 max_rgb.Red = dwR;
979 if(max_rgb.Green < dwG)
980 max_rgb.Green = dwG;
981 if(max_rgb.Blue < dwB)
982 max_rgb.Blue = dwB;
983
984 if(min_rgb.Red > dwR)
985 min_rgb.Red = dwR;
986 if(min_rgb.Green > dwG)
987 min_rgb.Green = dwG;
988 if(min_rgb.Blue > dwB)
989 min_rgb.Blue = dwB;
990 }
991
992 DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
993 max_rgb.Red, max_rgb.Red, max_rgb.Green,
994 max_rgb.Green, max_rgb.Blue, max_rgb.Blue );
995 DBG(_DBG_INFO2, "MIN(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
996 min_rgb.Red, min_rgb.Red, min_rgb.Green,
997 min_rgb.Green, min_rgb.Blue, min_rgb.Blue );
998
999 /* on CIS scanner, we use the min value, on CCD the max value
1000 * for adjusting the gain
1001 */
1002 tmp_rgb = max_rgb;
1003 if( usb_IsCISDevice(dev))
1004 tmp_rgb = min_rgb;
1005
1006 DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
1007 tmp_rgb.Red, tmp_rgb.Red, tmp_rgb.Green,
1008 tmp_rgb.Green, tmp_rgb.Blue, tmp_rgb.Blue);
1009
1010 /* m_dwIdealGain = IDEAL_GainNormal;
1011 */ /* min(min(rgb.wRed, rgb.wGreen), rgb.wBlue) */
1012
1013 regs[0x3b] = usb_GetNewGain( dev, tmp_rgb.Red, 0 );
1014 regs[0x3c] = usb_GetNewGain( dev, tmp_rgb.Green, 1 );
1015 regs[0x3d] = usb_GetNewGain( dev, tmp_rgb.Blue, 2 );
1016
1017 if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
1018
1019 SANE_Bool adj = SANE_FALSE;
1020
1021 /* on CIS devices, we can control the lamp off settings */
1022 if( usb_IsCISDevice(dev)) {
1023
1024 /* m_dwIdealGain = IDEAL_GainNormal;
1025 */
1026 if( adjLampSetting( dev, CHANNEL_red, tmp_rgb.Red, m_dwIdealGain,
1027 hw->red_lamp_on, &hw->red_lamp_off )) {
1028 adj = SANE_TRUE;
1029 }
1030
1031 if( adjLampSetting( dev, CHANNEL_green, tmp_rgb.Green, m_dwIdealGain,
1032 hw->green_lamp_on, &hw->green_lamp_off )) {
1033 adj = SANE_TRUE;
1034 }
1035
1036 if( adjLampSetting( dev, CHANNEL_blue, tmp_rgb.Blue, m_dwIdealGain,
1037 hw->blue_lamp_on, &hw->blue_lamp_off)){
1038 adj = SANE_TRUE;
1039 }
1040
1041 /* on any adjustment, set the registers... */
1042 if( adj ) {
1043 usb_AdjustLamps( dev, SANE_TRUE );
1044
1045 if( i < _MAX_GAIN_LOOPS )
1046 goto TOGAIN;
1047 }
1048
1049 } else {
1050
1051 if((!regs[0x3b] ||
1052 !regs[0x3c] || !regs[0x3d]) && dMCLK > min_mclk) {
1053
1054 scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
1055 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1056
1057 adj = SANE_TRUE;
1058
1059 } else if(((regs[0x3b] == 63) || (regs[0x3c] == 63) ||
1060 (regs[0x3d] == 63)) && (dMCLK < 10)) {
1061
1062 scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
1063 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1064
1065 adj = SANE_TRUE;
1066 }
1067
1068 if( adj ) {
1069 if( i < _MAX_GAIN_LOOPS )
1070 goto TOGAIN;
1071 }
1072 }
1073
1074 } else {
1075
1076 /* for MODEL KaoHsiung 1200 scanner multi-straight-line bug at
1077 * 1200 dpi color mode
1078 */
1079 if( hw->motorModel == MODEL_KaoHsiung &&
1080 scaps->bCCD == kNEC3778 && dMCLK>= 5.5 && !regs[0x3c]){
1081
1082 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1083 scanning->sParam.dMCLK = dMCLK = dMCLK - 1.5;
1084 goto TOGAIN;
1085
1086 } else if( hw->motorModel == MODEL_HuaLien &&
1087 scaps->bCCD == kNEC3799 && fRepeatITA ) {
1088
1089 if((!regs[0x3b] ||
1090 !regs[0x3c] || !regs[0x3d]) && dMCLK > 3.0) {
1091
1092 scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
1093 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1094 goto TOGAIN;
1095
1096 } else if(((regs[0x3b] == 63) || (regs[0x3c] == 63) ||
1097 (regs[0x3d] == 63)) && (dMCLK < 10)) {
1098
1099 scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
1100 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1101 goto TOGAIN;
1102 }
1103 bMaxITA = (u_char)floor((dMCLK + 1) / 2);
1104 fRepeatITA = SANE_FALSE;
1105 }
1106 }
1107
1108 } else {
1109
1110 u_short w_max = 0, w_min = 0xffff, w_tmp;
1111
1112 for( dw = start; dw < end; dw++ ) {
1113 if( w_max < ((u_short*)scanbuf)[dw])
1114 w_max = ((u_short*)scanbuf)[dw];
1115 if( w_min > ((u_short*)scanbuf)[dw])
1116 w_min = ((u_short*)scanbuf)[dw];
1117 }
1118
1119 w_tmp = w_max;
1120 if( usb_IsCISDevice(dev))
1121 w_tmp = w_min;
1122
1123 regs[0x3b] =
1124 regs[0x3c] =
1125 regs[0x3d] = usb_GetNewGain(dev, w_tmp, 0);
1126
1127 DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max );
1128 DBG(_DBG_INFO2, "MIN(G)= 0x%04x(%u)\n", w_min, w_min );
1129 DBG(_DBG_INFO2, "CUR(G)= 0x%04x(%u)\n", w_tmp, w_tmp );
1130
1131 /* m_dwIdealGain = IDEAL_GainNormal;
1132 */
1133 if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
1134
1135 SANE_Bool adj = SANE_FALSE;
1136
1137 /* on CIS devices, we can control the lamp off settings */
1138 if( usb_IsCISDevice(dev)) {
1139
1140 if( adjLampSetting( dev, CHANNEL_green, w_tmp, m_dwIdealGain,
1141 hw->green_lamp_on, &hw->green_lamp_off )) {
1142 adj = SANE_TRUE;
1143 }
1144
1145 /* on any adjustment, set the registers... */
1146 if( adj ) {
1147 usb_AdjustLamps( dev, SANE_TRUE );
1148
1149 if( i < _MAX_GAIN_LOOPS )
1150 goto TOGAIN;
1151 }
1152
1153 } else {
1154
1155 if( !regs[0x3b] && (dMCLK > min_mclk)) {
1156
1157 scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
1158 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1159
1160 adj = SANE_TRUE;
1161
1162 } else if((regs[0x3b] == 63) && (dMCLK < 20)) {
1163
1164 scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
1165 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
1166
1167 adj = SANE_TRUE;
1168 }
1169
1170 if( adj ) {
1171 if( i < _MAX_GAIN_LOOPS )
1172 goto TOGAIN;
1173 }
1174 }
1175 }
1176 }
1177 }
1178
1179 DBG( _DBG_INFO2, "REG[0x3b] = %u\n", regs[0x3b] );
1180 DBG( _DBG_INFO2, "REG[0x3c] = %u\n", regs[0x3c] );
1181 DBG( _DBG_INFO2, "REG[0x3d] = %u\n", regs[0x3d] );
1182
1183 DBG( _DBG_INFO2, "red_lamp_on = %u\n", hw->red_lamp_on );
1184 DBG( _DBG_INFO2, "red_lamp_off = %u\n", hw->red_lamp_off );
1185 DBG( _DBG_INFO2, "green_lamp_on = %u\n", hw->green_lamp_on );
1186 DBG( _DBG_INFO2, "green_lamp_off = %u\n", hw->green_lamp_off );
1187 DBG( _DBG_INFO2, "blue_lamp_on = %u\n", hw->blue_lamp_on );
1188 DBG( _DBG_INFO2, "blue_lamp_off = %u\n", hw->blue_lamp_off );
1189
1190 DBG( _DBG_INFO, "usb_AdjustGain() done.\n" );
1191 return SANE_TRUE;
1192 }
1193
1194 /** usb_GetNewOffset
1195 * @param pdwSum -
1196 * @param pdwDiff -
1197 * @param pcOffset -
1198 * @param pIdeal -
1199 * @param channel -
1200 * @param cAdjust -
1201 */
usb_GetNewOffset(Plustek_Device * dev,u_long * pdwSum,u_long * pdwDiff,signed char * pcOffset,u_char * pIdeal,u_long channel,signed char cAdjust)1202 static void usb_GetNewOffset( Plustek_Device *dev, u_long *pdwSum, u_long *pdwDiff,
1203 signed char *pcOffset, u_char *pIdeal,
1204 u_long channel, signed char cAdjust )
1205 {
1206 /* IDEAL_Offset is currently set to 0x1000 = 4096 */
1207 u_long dwIdealOffset = IDEAL_Offset;
1208
1209 if( pdwSum[channel] > dwIdealOffset ) {
1210
1211 /* Over ideal value */
1212 pdwSum[channel] -= dwIdealOffset;
1213 if( pdwSum[channel] < pdwDiff[channel] ) {
1214 /* New offset is better than old one */
1215 pdwDiff[channel] = pdwSum[channel];
1216 pIdeal[channel] = dev->usbDev.a_bRegs[0x38 + channel];
1217 }
1218 pcOffset[channel] -= cAdjust;
1219
1220 } else {
1221
1222 /* Below than ideal value */
1223 pdwSum[channel] = dwIdealOffset - pdwSum [channel];
1224 if( pdwSum[channel] < pdwDiff[channel] ) {
1225 /* New offset is better than old one */
1226 pdwDiff[channel] = pdwSum[channel];
1227 pIdeal[channel] = dev->usbDev.a_bRegs[0x38 + channel];
1228 }
1229 pcOffset[channel] += cAdjust;
1230 }
1231
1232 if( pcOffset[channel] >= 0 )
1233 dev->usbDev.a_bRegs[0x38 + channel] = pcOffset[channel];
1234 else
1235 dev->usbDev.a_bRegs[0x38 + channel] = (u_char)(32 - pcOffset[channel]);
1236 }
1237
1238 /** usb_AdjustOffset
1239 * function to perform the "coarse calibration step" part 2.
1240 * We scan reference image pixels to determine the optimum coarse offset settings
1241 * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
1242 * applied at the line rate during normal scanning.
1243 * On CIS based devices, we switch the light off, on CCD devices, we use the optical
1244 * black pixels.
1245 * Affects register 0x38, 0x39 and 0x3a
1246 */
usb_AdjustOffset(Plustek_Device * dev)1247 static SANE_Bool usb_AdjustOffset( Plustek_Device *dev )
1248 {
1249 char tmp[40];
1250 signed char cAdjust = 16;
1251 signed char cOffset[3];
1252 u_char bExpect[3];
1253 int i;
1254 u_long dw, dwPixels;
1255 u_long dwDiff[3], dwSum[3];
1256
1257 HWDef *hw = &dev->usbDev.HwSetting;
1258 u_char *regs = dev->usbDev.a_bRegs;
1259 u_long *scanbuf = dev->scanning.pScanBuffer;
1260
1261 if( usb_IsEscPressed())
1262 return SANE_FALSE;
1263
1264 DBG( _DBG_INFO, "#########################\n" );
1265 DBG( _DBG_INFO, "usb_AdjustOffset()\n" );
1266 if((dev->adj.rofs != -1) &&
1267 (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
1268 regs[0x38] = (dev->adj.rofs & 0x3f);
1269 regs[0x39] = (dev->adj.gofs & 0x3f);
1270 regs[0x3a] = (dev->adj.bofs & 0x3f);
1271 DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
1272 return SANE_TRUE;
1273 }
1274
1275 m_ScanParam.Size.dwLines = 1;
1276 m_ScanParam.Size.dwPixels = 2550;
1277
1278 if( usb_IsCISDevice(dev))
1279 dwPixels = m_ScanParam.Size.dwPixels;
1280 else
1281 dwPixels = (u_long)(hw->bOpticBlackEnd - hw->bOpticBlackStart );
1282
1283 m_ScanParam.Size.dwPixels = 2550;
1284 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2 *
1285 m_ScanParam.bChannels;
1286 if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
1287 m_ScanParam.Size.dwBytes *= 3;
1288
1289 m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL /
1290 dev->usbDev.Caps.OpticDpi.x);
1291 m_ScanParam.bCalibration = PARAM_Offset;
1292 m_ScanParam.dMCLK = dMCLK;
1293
1294 dwDiff[0] = dwDiff[1] = dwDiff[2] = 0xffff;
1295 cOffset[0] = cOffset[1] = cOffset[2] = 0;
1296 bExpect[0] = bExpect[1] = bExpect[2] = 0;
1297
1298 regs[0x38] = regs[0x39] = regs[0x3a] = 0;
1299
1300 if( usb_IsCISDevice(dev)) {
1301 /*
1302 * if we have dark shading strip, there's no need to switch
1303 * the lamp off
1304 */
1305 if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
1306
1307 usb_ModuleToHome( dev, SANE_TRUE );
1308 usb_ModuleMove ( dev, MOVE_Forward,
1309 (u_long)dev->usbDev.pSource->DarkShadOrgY );
1310
1311 regs[0x45] &= ~0x10;
1312
1313 } else {
1314
1315 /* switch lamp off to read dark data... */
1316 regs[0x29] = 0;
1317 usb_switchLamp( dev, SANE_FALSE );
1318 }
1319 }
1320
1321 if( 0 == dwPixels ) {
1322 DBG( _DBG_ERROR, "OpticBlackEnd = OpticBlackStart!!!\n" );
1323 return SANE_FALSE;
1324 }
1325
1326 if( !usb_SetScanParameters( dev, &m_ScanParam )) {
1327 DBG( _DBG_ERROR, "usb_AdjustOffset() failed\n" );
1328 return SANE_FALSE;
1329 }
1330
1331 i = 0;
1332
1333 DBG( _DBG_INFO2, "S.dwPixels = %lu\n", m_ScanParam.Size.dwPixels );
1334 DBG( _DBG_INFO2, "dwPixels = %lu\n", dwPixels );
1335 DBG( _DBG_INFO2, "dwPhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
1336 DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
1337
1338 while( cAdjust ) {
1339
1340 /*
1341 * read data (a white calibration strip - hopefully ;-)
1342 */
1343 if((!usb_ScanBegin(dev, SANE_FALSE)) ||
1344 (!usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes)) ||
1345 !usb_ScanEnd( dev )) {
1346 DBG( _DBG_ERROR, "usb_AdjustOffset() failed\n" );
1347 return SANE_FALSE;
1348 }
1349
1350 sprintf( tmp, "coarse-off-%u.raw", i++ );
1351
1352 #ifdef SWAP_COARSE
1353 if(usb_HostSwap())
1354 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
1355 #endif
1356 dumpPicInit(&m_ScanParam, tmp);
1357 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
1358
1359 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
1360
1361 dwSum[0] = dwSum[1] = dwSum[2] = 0;
1362
1363 for (dw = 0; dw < dwPixels; dw++) {
1364 #ifndef SWAP_COARSE
1365 dwSum[0] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[0]);
1366 dwSum[1] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[1]);
1367 dwSum[2] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[2]);
1368 #else
1369 dwSum[0] += ((RGBUShortDef*)scanbuf)[dw].Red;
1370 dwSum[1] += ((RGBUShortDef*)scanbuf)[dw].Green;
1371 dwSum[2] += ((RGBUShortDef*)scanbuf)[dw].Blue;
1372 #endif
1373 }
1374
1375 DBG( _DBG_INFO2, "RedSum = %lu, ave = %lu\n",
1376 dwSum[0], dwSum[0] /dwPixels );
1377 DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu\n",
1378 dwSum[1], dwSum[1] /dwPixels );
1379 DBG( _DBG_INFO2, "BlueSum = %lu, ave = %lu\n",
1380 dwSum[2], dwSum[2] /dwPixels );
1381
1382 /* do averaging for each channel */
1383 dwSum[0] /= dwPixels;
1384 dwSum[1] /= dwPixels;
1385 dwSum[2] /= dwPixels;
1386
1387 usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 0, cAdjust );
1388 usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 1, cAdjust );
1389 usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 2, cAdjust );
1390
1391 DBG( _DBG_INFO2, "RedExpect = %u\n", bExpect[0] );
1392 DBG( _DBG_INFO2, "GreenExpect = %u\n", bExpect[1] );
1393 DBG( _DBG_INFO2, "BlueExpect = %u\n", bExpect[2] );
1394
1395 } else {
1396 dwSum[0] = 0;
1397
1398 for( dw = 0; dw < dwPixels; dw++ ) {
1399 #ifndef SWAP_COARSE
1400 dwSum[0] += (u_long)_HILO2WORD(((HiLoDef*)scanbuf)[dw]);
1401 #else
1402 dwSum[0] += ((u_short*)scanbuf)[dw];
1403 #endif
1404 }
1405 dwSum [0] /= dwPixels;
1406 usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 0, cAdjust );
1407 regs[0x3a] = regs[0x39] = regs[0x38];
1408
1409 DBG(_DBG_INFO2,"Sum = %lu, ave = %lu\n",dwSum[0],dwSum[0]/dwPixels);
1410 DBG(_DBG_INFO2,"Expect = %u\n", bExpect[0]);
1411 }
1412
1413 _UIO(sanei_lm983x_write(dev->fd, 0x38, ®s[0x38], 3, SANE_TRUE));
1414 cAdjust >>= 1;
1415 }
1416
1417 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
1418 regs[0x38] = bExpect[0];
1419 regs[0x39] = bExpect[1];
1420 regs[0x3a] = bExpect[2];
1421 } else {
1422
1423 regs[0x38] = regs[0x39] = regs[0x3a] = bExpect[0];
1424 }
1425
1426 DBG( _DBG_INFO2, "REG[0x38] = %u\n", regs[0x38] );
1427 DBG( _DBG_INFO2, "REG[0x39] = %u\n", regs[0x39] );
1428 DBG( _DBG_INFO2, "REG[0x3a] = %u\n", regs[0x3a] );
1429 DBG( _DBG_INFO, "usb_AdjustOffset() done.\n" );
1430
1431 /* switch it on again on CIS based scanners */
1432 if( usb_IsCISDevice(dev)) {
1433
1434 if( dev->usbDev.pSource->DarkShadOrgY < 0 ) {
1435 regs[0x29] = hw->bReg_0x29;
1436 usb_switchLamp( dev, SANE_TRUE );
1437 usbio_WriteReg( dev->fd, 0x29, regs[0x29]);
1438 }
1439 }
1440
1441 return SANE_TRUE;
1442 }
1443
1444 /** this function tries to find out some suitable values for the dark
1445 * fine calibration. If the device owns a black calibration strip
1446 * the data is simply copied. If not, then the white strip is read
1447 * with the lamp switched off...
1448 */
usb_GetDarkShading(Plustek_Device * dev,u_short * pwDest,HiLoDef * pSrce,u_long dwPixels,u_long dwAdd,int iOffset)1449 static void usb_GetDarkShading( Plustek_Device *dev, u_short *pwDest,
1450 HiLoDef *pSrce, u_long dwPixels,
1451 u_long dwAdd, int iOffset )
1452 {
1453 u_long dw;
1454 u_long dwSum[2];
1455 DCapsDef *scaps = &dev->usbDev.Caps;
1456 HWDef *hw = &dev->usbDev.HwSetting;
1457
1458 if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
1459
1460 u_short w;
1461 int wtmp;
1462
1463 /* here we use the source buffer + a static offset */
1464 for (dw = 0; dw < dwPixels; dw++, pSrce += dwAdd)
1465 {
1466 #ifndef SWAP_FINE
1467 wtmp = ((int)_PHILO2WORD(pSrce) + iOffset);
1468 #else
1469 wtmp = ((int)_PLOHI2WORD(pSrce) + iOffset);
1470 #endif
1471 if( wtmp < 0 )
1472 wtmp = 0;
1473
1474 if( wtmp > 0xffff )
1475 wtmp = 0xffff;
1476
1477 w = (u_short)wtmp;
1478
1479 #ifndef SWAP_FINE
1480 pwDest[dw] = _LOBYTE(w) * 256 + _HIBYTE(w);
1481 #else
1482 pwDest[dw] = w;
1483 #endif
1484 }
1485 }
1486 else
1487 {
1488 dwSum[0] = dwSum[1] = 0;
1489 if( hw->bSensorConfiguration & 0x04 ) {
1490
1491 /* Even/Odd CCD */
1492 for( dw = 0; dw < dwPixels; dw++, pSrce += dwAdd ) {
1493 #ifndef SWAP_FINE
1494 dwSum[dw & 1] += (u_long)_PHILO2WORD(pSrce);
1495 #else
1496 dwSum[dw & 1] += (u_long)_PLOHI2WORD(pSrce);
1497 #endif
1498 }
1499 dwSum[0] /= ((dwPixels + 1UL) >> 1);
1500 dwSum[1] /= (dwPixels >> 1);
1501
1502 if( /*Registry.GetEvenOdd() == 1 ||*/ scaps->bPCB == 2)
1503 {
1504 dwSum[0] = dwSum[1] = (dwSum[0] + dwSum[1]) / 2;
1505 }
1506
1507 dwSum[0] = (int)dwSum[0] + iOffset;
1508 dwSum[1] = (int)dwSum[1] + iOffset;
1509
1510 if((int)dwSum[0] < 0)
1511 dwSum[0] = 0;
1512
1513 if((int)dwSum[1] < 0)
1514 dwSum[1] = 0;
1515 #ifndef SWAP_FINE
1516 dwSum[0] = (u_long)_LOBYTE(_LOWORD(dwSum[0])) * 256UL +
1517 _HIBYTE(_LOWORD(dwSum[0]));
1518 dwSum[1] = (u_long)_LOBYTE(_LOWORD(dwSum[1])) * 256UL +
1519 _HIBYTE(_LOWORD(dwSum[1]));
1520 #else
1521 dwSum[0] = (u_long)_LOWORD(dwSum[0]);
1522 dwSum[1] = (u_long)_LOWORD(dwSum[1]);
1523 #endif
1524
1525 for( dw = 0; dw < dwPixels; dw++ )
1526 pwDest[dw] = (u_short)dwSum[dw & 1];
1527 } else {
1528
1529 /* Standard CCD */
1530
1531 /* do some averaging on the line */
1532 for( dw = 0; dw < dwPixels; dw++, pSrce += dwAdd ) {
1533 #ifndef SWAP_FINE
1534 dwSum[0] += (u_long)_PHILO2WORD(pSrce);
1535 #else
1536 dwSum[0] += (u_long)_PLOHI2WORD(pSrce);
1537 #endif
1538 }
1539
1540 dwSum[0] /= dwPixels;
1541
1542 /* add our offset... */
1543 dwSum[0] = (int)dwSum[0] + iOffset;
1544 if((int)dwSum[0] < 0)
1545 dwSum[0] = 0;
1546 #ifndef SWAP_FINE
1547 dwSum[0] = (u_long)_LOBYTE(_LOWORD(dwSum[0])) * 256UL +
1548 _HIBYTE(_LOWORD(dwSum[0]));
1549 #else
1550 dwSum[0] = (u_long)_LOWORD(dwSum[0]);
1551 #endif
1552
1553 /* fill the shading data */
1554 for( dw = 0; dw < dwPixels; dw++ )
1555 pwDest[dw] = (u_short)dwSum[0];
1556 }
1557 }
1558 #ifdef SWAP_FINE
1559 if(usb_HostSwap())
1560 usb_Swap( pwDest, dwPixels *2 );
1561 #endif
1562 }
1563
1564 /** usb_AdjustDarkShading
1565 * fine calibration part 1 - read the black calibration area and write
1566 * the black line data to the offset coefficient data in Merlins' DRAM
1567 * If there's no black line available, we can use the min pixel value
1568 * from coarse calibration...
1569 */
usb_AdjustDarkShading(Plustek_Device * dev)1570 static SANE_Bool usb_AdjustDarkShading( Plustek_Device *dev )
1571 {
1572 char tmp[40];
1573 ScanDef *scanning = &dev->scanning;
1574 DCapsDef *scaps = &dev->usbDev.Caps;
1575 HWDef *hw = &dev->usbDev.HwSetting;
1576 u_long *scanbuf = scanning->pScanBuffer;
1577 u_char *regs = dev->usbDev.a_bRegs;
1578
1579 if( usb_IsEscPressed())
1580 return SANE_FALSE;
1581
1582 if( scaps->workaroundFlag & _WAF_SKIP_FINE )
1583 return SANE_TRUE;
1584
1585 DBG( _DBG_INFO, "#########################\n" );
1586 DBG( _DBG_INFO, "usb_AdjustDarkShading()\n" );
1587 DBG( _DBG_INFO2, "* MCLK = %f (scanparam-MCLK=%f)\n",
1588 dMCLK, scanning->sParam.dMCLK );
1589
1590 usb_PrepareFineCal( dev, &m_ScanParam, 0 );
1591
1592 m_ScanParam.Size.dwLines = 1; /* for gain */
1593 m_ScanParam.bCalibration = PARAM_DarkShading;
1594
1595 if( _LM9831 == hw->chip ) {
1596
1597 m_ScanParam.UserDpi.x = usb_SetAsicDpiX( dev, m_ScanParam.UserDpi.x);
1598 if( m_ScanParam.UserDpi.x < 100)
1599 m_ScanParam.UserDpi.x = 150;
1600
1601 /* Now DPI X is physical */
1602 m_ScanParam.Origin.x = m_ScanParam.Origin.x %
1603 (u_short)m_dHDPIDivider;
1604 m_ScanParam.Size.dwPixels = (u_long)scaps->Normal.Size.x *
1605 m_ScanParam.UserDpi.x / 300UL;
1606 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
1607 2UL * m_ScanParam.bChannels;
1608 m_dwPixels = scanning->sParam.Size.dwPixels *
1609 m_ScanParam.UserDpi.x / scanning->sParam.UserDpi.x;
1610
1611 if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
1612 m_ScanParam.Size.dwBytes *= 3;
1613 }
1614
1615 /* if we have dark shading strip, there's no need to switch
1616 * the lamp off
1617 */
1618 if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
1619
1620 usb_ModuleToHome( dev, SANE_TRUE );
1621 usb_ModuleMove ( dev, MOVE_Forward,
1622 (u_long)dev->usbDev.pSource->DarkShadOrgY );
1623 } else {
1624
1625 /* switch lamp off to read dark data... */
1626 regs[0x29] = 0;
1627 usb_switchLamp( dev, SANE_FALSE );
1628 }
1629
1630 usb_SetScanParameters( dev, &m_ScanParam );
1631
1632 if((!usb_ScanBegin(dev, SANE_FALSE)) ||
1633 (!usb_ScanReadImage(dev,scanbuf, m_ScanParam.Size.dwPhyBytes)) ||
1634 (!usb_ScanEnd( dev ))) {
1635
1636 /* on error, reset the lamp settings*/
1637 regs[0x29] = hw->bReg_0x29;
1638 usb_switchLamp( dev, SANE_TRUE );
1639 usbio_WriteReg( dev->fd, 0x29, regs[0x29] );
1640
1641 DBG( _DBG_ERROR, "usb_AdjustDarkShading() failed\n" );
1642 return SANE_FALSE;
1643 }
1644
1645 /* set illumination mode and switch lamp on again
1646 */
1647 regs[0x29] = hw->bReg_0x29;
1648 usb_switchLamp( dev, SANE_TRUE );
1649
1650 if( !usbio_WriteReg( dev->fd, 0x29, regs[0x29])) {
1651 DBG( _DBG_ERROR, "usb_AdjustDarkShading() failed\n" );
1652 return SANE_FALSE;
1653 }
1654
1655 #ifdef SWAP_FINE
1656 if(usb_HostSwap())
1657 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
1658 #endif
1659
1660 sprintf( tmp, "fine-black.raw" );
1661
1662 dumpPicInit(&m_ScanParam, tmp);
1663 dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
1664
1665 usleep(500 * 1000); /* Warm up lamp again */
1666
1667 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
1668
1669 if( usb_IsCISDevice(dev)) {
1670
1671 usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
1672 m_ScanParam.Size.dwPhyPixels, 1,
1673 scanning->sParam.swOffset[0]);
1674
1675 usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
1676 (HiLoDef*)scanbuf + m_ScanParam.Size.dwPhyPixels,
1677 m_ScanParam.Size.dwPhyPixels, 1, scanning->sParam.swOffset[1]);
1678
1679 usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
1680 (HiLoDef*)scanbuf + m_ScanParam.Size.dwPhyPixels * 2,
1681 m_ScanParam.Size.dwPhyPixels, 1, scanning->sParam.swOffset[2]);
1682
1683 } else {
1684
1685 usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
1686 m_ScanParam.Size.dwPhyPixels, 3,
1687 scanning->sParam.swOffset[0]);
1688 usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
1689 (HiLoDef*)scanbuf + 1, m_ScanParam.Size.dwPhyPixels,
1690 3, scanning->sParam.swOffset[1]);
1691 usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
1692 (HiLoDef*)scanbuf + 2, m_ScanParam.Size.dwPhyPixels,
1693 3, scanning->sParam.swOffset[2]);
1694 }
1695 } else {
1696
1697 usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
1698 m_ScanParam.Size.dwPhyPixels, 1,
1699 scanning->sParam.swOffset[1]);
1700
1701 memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
1702 a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
1703 memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
1704 a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
1705 }
1706
1707 regs[0x45] |= 0x10;
1708
1709 usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
1710 scanning->sParam.bDataType == SCANDATATYPE_Color?1:0);
1711 return SANE_TRUE;
1712 }
1713
1714 /** function to remove the brightest values out of each row
1715 * @param dev - the almighty device structure.
1716 * @param sp - is a pointer to the scanparam structure used for
1717 * scanning the shading lines.
1718 * @param hilight - defines the number of values to skip.
1719 * @param shading_lines - defines the overall number of shading lines.
1720 */
usb_CalSortHighlight(Plustek_Device * dev,ScanParam * sp,u_long hilight,u_long shading_lines)1721 static void usb_CalSortHighlight( Plustek_Device *dev, ScanParam *sp,
1722 u_long hilight, u_long shading_lines )
1723 {
1724 ScanDef *scan = &dev->scanning;
1725 u_short r, g, b;
1726 u_long lines, w, x;
1727 RGBUShortDef *pw, *rgb;
1728
1729 if( hilight == 0 )
1730 return;
1731
1732 rgb = (RGBUShortDef*)scan->pScanBuffer;
1733
1734 /* do it for all relevant lines */
1735 for( lines = hilight, rgb = rgb + sp->Size.dwPhyPixels * lines;
1736 lines < shading_lines; lines++, rgb += sp->Size.dwPhyPixels ) {
1737
1738 /* scan the complete line */
1739 for( x = 0; x < sp->Size.dwPhyPixels; x++ ) {
1740
1741 /* reference is the first scanline */
1742 pw = (RGBUShortDef*)scan->pScanBuffer;
1743 r = rgb[x].Red;
1744 g = rgb[x].Green;
1745 b = rgb[x].Blue;
1746
1747 for( w = 0; w < hilight; w++, pw += sp->Size.dwPhyPixels ) {
1748
1749 if( r > pw[x].Red )
1750 _SWAP( r, pw[x].Red );
1751
1752 if( g > pw[x].Green )
1753 _SWAP( g, pw[x].Green );
1754
1755 if( b > pw[x].Blue )
1756 _SWAP( b, pw[x].Blue );
1757 }
1758 rgb[x].Red = r;
1759 rgb[x].Green = g;
1760 rgb[x].Blue = b;
1761 }
1762 }
1763 }
1764
1765 /** function to remove the brightest values out of each row
1766 * @param dev - the almighty device structure.
1767 * @param sp - is a pointer to the scanparam structure used for
1768 * scanning the shading lines.
1769 * @param hilight - defines the number of values to skip.
1770 * @param shading_lines - defines the overall number of shading lines.
1771 */
usb_CalSortShadow(Plustek_Device * dev,ScanParam * sp,u_long hilight,u_long shadow,u_long shading_lines)1772 static void usb_CalSortShadow( Plustek_Device *dev, ScanParam *sp,
1773 u_long hilight, u_long shadow, u_long shading_lines )
1774 {
1775 ScanDef *scan = &dev->scanning;
1776 u_short r, g, b;
1777 u_long lines, w, x;
1778 RGBUShortDef *pw, *rgb;
1779
1780 if( shadow == 0 )
1781 return;
1782
1783 rgb = (RGBUShortDef*)scan->pScanBuffer;
1784
1785 for( lines = hilight, rgb = rgb + sp->Size.dwPhyPixels * lines;
1786 lines < shading_lines-shadow; lines++, rgb += sp->Size.dwPhyPixels ) {
1787
1788 for (x = 0; x < sp->Size.dwPhyPixels; x++) {
1789
1790 pw = ((RGBUShortDef*)scan->pScanBuffer) + (shading_lines - shadow) *
1791 sp->Size.dwPhyPixels;
1792 r = rgb[x].Red;
1793 g = rgb[x].Green;
1794 b = rgb[x].Blue;
1795
1796 for( w = 0; w < shadow; w++, pw += sp->Size.dwPhyPixels ) {
1797 if( r < pw[x].Red )
1798 _SWAP( r, pw[x].Red );
1799 if( g < pw[x].Green )
1800 _SWAP( g, pw [x].Green );
1801 if( b > pw[x].Blue )
1802 _SWAP( b, pw[x].Blue );
1803 }
1804 rgb[x].Red = r;
1805 rgb[x].Green = g;
1806 rgb[x].Blue = b;
1807 }
1808 }
1809 }
1810
usb_procHighlightAndShadow(Plustek_Device * dev,ScanParam * sp,u_long hilight,u_long shadow,u_long shading_lines)1811 static void usb_procHighlightAndShadow( Plustek_Device *dev, ScanParam *sp,
1812 u_long hilight, u_long shadow, u_long shading_lines )
1813 {
1814 ScanDef *scan = &dev->scanning;
1815 u_long lines, x;
1816 u_long *pr, *pg, *pb;
1817 RGBUShortDef *rgb;
1818
1819 pr = (u_long*)((u_char*)scan->pScanBuffer + sp->Size.dwPhyBytes * shading_lines);
1820 pg = pr + sp->Size.dwPhyPixels;
1821 pb = pg + sp->Size.dwPhyPixels;
1822
1823 memset(pr, 0, sp->Size.dwPhyPixels * sizeof(*pr) * 3UL);
1824
1825 /* Sort hilight */
1826 usb_CalSortHighlight(dev, sp, hilight, shading_lines);
1827
1828 /* Sort shadow */
1829 usb_CalSortShadow(dev, sp, hilight, shadow, shading_lines);
1830
1831 rgb = (RGBUShortDef*)scan->pScanBuffer;
1832 rgb += sp->Size.dwPhyPixels * hilight;
1833
1834 /* Sum */
1835 for( lines = hilight; lines < (shading_lines-shadow); lines++ ) {
1836
1837 for( x = 0; x < sp->Size.dwPhyPixels; x++ ) {
1838 pr[x] += rgb[x].Red;
1839 pg[x] += rgb[x].Green;
1840 pb[x] += rgb[x].Blue;
1841 }
1842
1843 rgb += sp->Size.dwPhyPixels;
1844 }
1845 }
1846
1847 /** usb_AdjustWhiteShading
1848 * fine calibration part 2 - read the white calibration area and calculate
1849 * the gain coefficient for each pixel
1850 */
usb_AdjustWhiteShading(Plustek_Device * dev)1851 static SANE_Bool usb_AdjustWhiteShading( Plustek_Device *dev )
1852 {
1853 char tmp[40];
1854 ScanDef *scan = &dev->scanning;
1855 DCapsDef *scaps = &dev->usbDev.Caps;
1856 HWDef *hw = &dev->usbDev.HwSetting;
1857 u_long *pBuf = scan->pScanBuffer;
1858 u_long dw, dwLines, dwRead;
1859 u_long shading_lines;
1860 MonoWordDef *pValue;
1861 u_short *m_pAvMono;
1862 u_long *pdw, *m_pSum;
1863 u_short hilight, shadow;
1864 int i;
1865 SANE_Bool swap = usb_HostSwap();
1866
1867 if( scaps->workaroundFlag & _WAF_SKIP_FINE )
1868 return SANE_TRUE;
1869
1870 DBG( _DBG_INFO, "#########################\n" );
1871 DBG( _DBG_INFO, "usb_AdjustWhiteShading()\n" );
1872
1873 m_pAvMono = (u_short*)scan->pScanBuffer;
1874
1875 if( usb_IsEscPressed())
1876 return SANE_FALSE;
1877
1878 usb_PrepareFineCal( dev, &m_ScanParam, 0 );
1879
1880 if( m_ScanParam.PhyDpi.x > 75)
1881 shading_lines = 64;
1882 else
1883 shading_lines = 32;
1884
1885 /* NOTE: hilight + shadow < shading_lines */
1886 hilight = 4;
1887 shadow = 4;
1888
1889 m_ScanParam.bCalibration = PARAM_WhiteShading;
1890 m_ScanParam.Size.dwLines = shading_lines;
1891
1892 if( _LM9831 == hw->chip ) {
1893
1894 m_ScanParam.UserDpi.x = usb_SetAsicDpiX( dev, m_ScanParam.UserDpi.x);
1895 if( m_ScanParam.UserDpi.x < 100 )
1896 m_ScanParam.UserDpi.x = 150;
1897
1898 /* Now DPI X is physical */
1899 m_ScanParam.Origin.x = m_ScanParam.Origin.x % (u_short)m_dHDPIDivider;
1900 m_ScanParam.Size.dwPixels = (u_long)scaps->Normal.Size.x * m_ScanParam.UserDpi.x / 300UL;
1901 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2UL * m_ScanParam.bChannels;
1902 if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
1903 m_ScanParam.Size.dwBytes *= 3;
1904
1905 m_dwPixels = scan->sParam.Size.dwPixels * m_ScanParam.UserDpi.x /
1906 scan->sParam.UserDpi.x;
1907
1908 dw = (u_long)(hw->wDRAMSize - 196 /*192 KiB*/) * 1024UL;
1909 for( dwLines = dw / m_ScanParam.Size.dwBytes;
1910 dwLines < m_ScanParam.Size.dwLines; m_ScanParam.Size.dwLines>>=1);
1911 }
1912
1913 /* goto the correct position again... */
1914 if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
1915
1916 usb_ModuleToHome( dev, SANE_TRUE );
1917 usb_ModuleMove ( dev, MOVE_Forward,
1918 (u_long)dev->usbDev.pSource->ShadingOriginY );
1919 }
1920
1921 sprintf( tmp, "fine-white.raw" );
1922 DBG( _DBG_INFO2, "FINE WHITE Calibration Strip: %s\n", tmp );
1923 DBG( _DBG_INFO2, "Shad.-Lines = %lu\n", shading_lines );
1924 DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines );
1925 DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
1926 DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
1927 DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x );
1928
1929 for( dw = shading_lines, dwRead = 0; dw; dw -= m_ScanParam.Size.dwLines ) {
1930
1931 if( usb_SetScanParameters( dev, &m_ScanParam ) &&
1932 usb_ScanBegin( dev, SANE_FALSE )) {
1933
1934 DBG(_DBG_INFO2,"TotalBytes = %lu\n",m_ScanParam.Size.dwTotalBytes);
1935 if( _LM9831 == hw->chip ) {
1936 /* Delay for white shading hold for 9831-1200 scanner */
1937 usleep(900000);
1938 }
1939
1940 if( usb_ScanReadImage( dev, (u_char*)pBuf + dwRead,
1941 m_ScanParam.Size.dwTotalBytes)) {
1942
1943 if( _LM9831 == hw->chip ) {
1944 /* Delay for white shading hold for 9831-1200 scanner */
1945 usleep(10000);
1946 }
1947
1948 if( 0 == dwRead ) {
1949 dumpPicInit(&m_ScanParam, tmp);
1950 }
1951
1952 dumpPic(tmp, (u_char*)pBuf + dwRead, m_ScanParam.Size.dwTotalBytes, 0);
1953
1954 if( usb_ScanEnd( dev )) {
1955 dwRead += m_ScanParam.Size.dwTotalBytes;
1956 continue;
1957 }
1958 }
1959 }
1960
1961 DBG( _DBG_ERROR, "usb_AdjustWhiteShading() failed\n" );
1962 return SANE_FALSE;
1963 }
1964
1965 m_pSum = (u_long*)((u_char*)pBuf + m_ScanParam.Size.dwPhyBytes * shading_lines);
1966
1967 /*
1968 * do some reordering on CIS based devices:
1969 * from RRRRRRR.... GGGGGGGG.... BBBBBBBBB, create RGB RGB RGB ...
1970 * to use the following code, originally written for CCD devices...
1971 */
1972 if( usb_IsCISDevice(dev)) {
1973
1974 u_short *dest, *src;
1975 u_long dww;
1976
1977 src = (u_short*)pBuf;
1978
1979 DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes );
1980 DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
1981 DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels );
1982 DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes );
1983 DBG( _DBG_INFO2, "Channels = %u\n", m_ScanParam.bChannels );
1984
1985 for( dwLines = shading_lines; dwLines; dwLines-- ) {
1986
1987 dest = a_wWhiteShading;
1988
1989 for( dw=dww=0; dw < m_ScanParam.Size.dwPhyPixels; dw++, dww+=3 ) {
1990
1991 dest[dww] = src[dw];
1992 dest[dww + 1] = src[m_ScanParam.Size.dwPhyPixels + dw];
1993 dest[dww + 2] = src[m_ScanParam.Size.dwPhyPixels * 2 + dw];
1994 }
1995
1996 /* copy line back ... */
1997 memcpy( src, dest, m_ScanParam.Size.dwPhyPixels * 3 * 2 );
1998 src = &src[m_ScanParam.Size.dwPhyPixels * 3];
1999 }
2000
2001 m_ScanParam.bChannels = 3;
2002 }
2003
2004 if( _LM9831 == hw->chip ) {
2005
2006 u_short *pwDest = (u_short*)pBuf;
2007 HiLoDef *pwSrce = (HiLoDef*)pBuf;
2008
2009 pwSrce += ((u_long)(scan->sParam.Origin.x-m_ScanParam.Origin.x) /
2010 (u_short)m_dHDPIDivider) *
2011 (scaps->OpticDpi.x / 300UL) * m_ScanParam.bChannels;
2012
2013 for( dwLines = shading_lines; dwLines; dwLines--) {
2014
2015 #ifdef SWAP_FINE
2016 if(usb_HostSwap()) {
2017 #endif
2018 for( dw = 0; dw < m_dwPixels * m_ScanParam.bChannels; dw++ )
2019 pwDest[dw] = _HILO2WORD(pwSrce[dw]);
2020 #ifdef SWAP_FINE
2021 } else {
2022 for( dw = 0; dw < m_dwPixels * m_ScanParam.bChannels; dw++ )
2023 pwDest[dw] = _LOHI2WORD(pwSrce[dw]);
2024 }
2025 #endif
2026 pwDest += (u_long)m_dwPixels * m_ScanParam.bChannels;
2027 pwSrce = (HiLoDef*)((u_char*)pwSrce + m_ScanParam.Size.dwPhyBytes);
2028 }
2029
2030 _SWAP(m_ScanParam.Size.dwPhyPixels, m_dwPixels);
2031 } else {
2032 /* Discard the status word and conv. the hi-lo order to intel format */
2033 u_short *pwDest = (u_short*)pBuf;
2034 HiLoDef *pwSrce = (HiLoDef*)pBuf;
2035
2036 for( dwLines = shading_lines; dwLines; dwLines-- ) {
2037
2038 #ifdef SWAP_FINE
2039 if(usb_HostSwap()) {
2040 #endif
2041 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels *
2042 m_ScanParam.bChannels; dw++) {
2043 pwDest[dw] = _HILO2WORD(pwSrce[dw]);
2044 }
2045 #ifdef SWAP_FINE
2046 } else {
2047 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels *
2048 m_ScanParam.bChannels; dw++) {
2049 pwDest[dw] = _LOHI2WORD(pwSrce[dw]);
2050 }
2051 }
2052 #endif
2053 pwDest += m_ScanParam.Size.dwPhyPixels * m_ScanParam.bChannels;
2054 pwSrce = (HiLoDef*)((u_char*)pwSrce + m_ScanParam.Size.dwPhyBytes);
2055 }
2056 }
2057
2058 if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
2059
2060 usb_procHighlightAndShadow(dev, &m_ScanParam, hilight, shadow, shading_lines);
2061
2062 pValue = (MonoWordDef*)a_wWhiteShading;
2063 pdw = (u_long*)m_pSum;
2064
2065 /* Software gain */
2066 if( scan->sParam.bSource != SOURCE_Negative ) {
2067
2068 for( i = 0; i < 3; i++ ) {
2069
2070 for(dw=m_ScanParam.Size.dwPhyPixels; dw; dw--,pValue++,pdw++) {
2071
2072 *pdw = *pdw * 1000 / ((shading_lines - hilight - shadow) *
2073 scan->sParam.swGain[i]);
2074 if(*pdw > 65535U)
2075 pValue->Mono = 65535U;
2076 else
2077 pValue->Mono = (u_short)*pdw;
2078
2079 if (pValue->Mono > 16384U)
2080 pValue->Mono = (u_short)(GAIN_Target * 16384U / pValue->Mono);
2081 else
2082 pValue->Mono = GAIN_Target;
2083
2084 #ifdef SWAP_FINE
2085 if( swap )
2086 #endif
2087 _SWAP(pValue->HiLo.bHi, pValue->HiLo.bLo);
2088 }
2089 }
2090 } else {
2091 for( dw = m_ScanParam.Size.dwPhyPixels*3; dw; dw--,pValue++,pdw++)
2092 pValue->Mono=(u_short)(*pdw/(shading_lines-hilight-shadow));
2093
2094 /* swapping will be done later in usb_ResizeWhiteShading() */
2095 }
2096 } else {
2097
2098 /* gray mode */
2099 u_short *pwAv, *pw;
2100 u_short w, wV;
2101
2102 memset( m_pSum, 0, m_ScanParam.Size.dwPhyPixels << 2 );
2103 if( hilight ) {
2104 for( dwLines = hilight,
2105 pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
2106 dwLines < shading_lines;
2107 dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels) {
2108
2109 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
2110
2111 pw = m_pAvMono;
2112 wV = pwAv [dw];
2113 for( w = 0; w < hilight; w++,
2114 pw += m_ScanParam.Size.dwPhyPixels ) {
2115 if( wV > pw[dw] )
2116 _SWAP( wV, pw[dw] );
2117 }
2118 pwAv[dw] = wV;
2119 }
2120 }
2121 }
2122
2123 /* Sort shadow */
2124 if (shadow) {
2125 for (dwLines = hilight, pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
2126 dwLines < (shading_lines - shadow); dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels)
2127 for (dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++)
2128 {
2129 pw = m_pAvMono + (shading_lines - shadow) * m_ScanParam.Size.dwPhyPixels;
2130 wV = pwAv [dw];
2131 for (w = 0; w < shadow; w++, pw += m_ScanParam.Size.dwPhyPixels)
2132 if (wV < pw [dw])
2133 _SWAP (wV, pw[dw]);
2134 pwAv [dw] = wV;
2135 }
2136 }
2137
2138 /* Sum */
2139 pdw = (u_long*)m_pSum;
2140
2141 for (dwLines = hilight,
2142 pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
2143 dwLines < (shading_lines - shadow);
2144 dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels) {
2145 for (dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++)
2146 pdw[dw] += pwAv[dw];
2147 }
2148
2149 /* Software gain */
2150 pValue = (MonoWordDef*)a_wWhiteShading;
2151 if( scan->sParam.bSource != SOURCE_Negative ) {
2152
2153 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++) {
2154
2155 pdw[dw] = pdw[dw] * 1000 /((shading_lines-hilight-shadow) *
2156 scan->sParam.swGain[1]);
2157 if( pdw[dw] > 65535U )
2158 pValue[dw].Mono = 65535;
2159 else
2160 pValue[dw].Mono = (u_short)pdw[dw];
2161
2162 if( pValue[dw].Mono > 16384U ) {
2163 pValue[dw].Mono = (u_short)(GAIN_Target * 16384U / pValue[dw].Mono);
2164 } else {
2165 pValue[dw].Mono = GAIN_Target;
2166 }
2167
2168 #ifdef SWAP_FINE
2169 if( swap )
2170 #endif
2171 _SWAP(pValue[dw].HiLo.bHi, pValue[dw].HiLo.bLo);
2172 }
2173
2174 } else{
2175
2176 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
2177 pValue[dw].Mono = (u_short)(pdw[dw] /
2178 (shading_lines - hilight - shadow));
2179
2180 /* swapping will be done later in usb_ResizeWhiteShading() */
2181 }
2182 }
2183 }
2184
2185 usb_SaveCalSetShading( dev, &m_ScanParam );
2186
2187 if( scan->sParam.bSource != SOURCE_Negative ) {
2188 usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
2189 scan->sParam.bDataType == SCANDATATYPE_Color?1:0);
2190 }
2191 return SANE_TRUE;
2192 }
2193
2194 /** for negative film only
2195 * we need to resize the gain to obtain bright white...
2196 */
usb_ResizeWhiteShading(double dAmp,u_short * pwShading,int iGain)2197 static void usb_ResizeWhiteShading( double dAmp, u_short *pwShading, int iGain )
2198 {
2199 u_long dw, dwAmp;
2200 u_short w;
2201
2202 DBG( _DBG_INFO2, "ResizeWhiteShading: dAmp=%.3f, iGain=%i\n", dAmp, iGain );
2203
2204 for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
2205
2206 dwAmp = (u_long)(GAIN_Target * 0x4000 /
2207 (pwShading[dw] + 1) * dAmp) * iGain / 1000;
2208
2209 if( dwAmp <= GAIN_Target)
2210 w = (u_short)dwAmp;
2211 else
2212 w = GAIN_Target;
2213
2214 #ifndef SWAP_FINE
2215 pwShading[dw] = (u_short)_LOBYTE(w) * 256 + _HIBYTE(w);
2216 #else
2217 pwShading[dw] = w;
2218 #endif
2219 }
2220
2221 #ifdef SWAP_FINE
2222 if( usb_HostSwap())
2223 usb_Swap( pwShading, m_ScanParam.Size.dwPhyPixels );
2224 #endif
2225 }
2226
2227 /** do the base settings for calibration scans
2228 */
2229 static void
usb_PrepareCalibration(Plustek_Device * dev)2230 usb_PrepareCalibration( Plustek_Device *dev )
2231 {
2232 ScanDef *scan = &dev->scanning;
2233 DCapsDef *scaps = &dev->usbDev.Caps;
2234 u_char *regs = dev->usbDev.a_bRegs;
2235
2236 usb_GetSWOffsetGain( dev );
2237
2238 memset( &m_ScanParam, 0, sizeof(ScanParam));
2239
2240 m_ScanParam.UserDpi = scaps->OpticDpi;
2241 m_ScanParam.PhyDpi = scaps->OpticDpi;
2242 m_ScanParam.bChannels = scan->sParam.bChannels;
2243 m_ScanParam.bBitDepth = 16;
2244 m_ScanParam.bSource = scan->sParam.bSource;
2245 m_ScanParam.Origin.y = 0;
2246
2247 if( scan->sParam.bDataType == SCANDATATYPE_Color )
2248 m_ScanParam.bDataType = SCANDATATYPE_Color;
2249 else
2250 m_ScanParam.bDataType = SCANDATATYPE_Gray;
2251
2252 usb_SetMCLK( dev, &m_ScanParam );
2253
2254 /* preset these registers offset/gain */
2255 regs[0x38] = regs[0x39] = regs[0x3a] = 0;
2256 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2257 regs[0x45] &= ~0x10;
2258
2259 memset( a_wWhiteShading, 0, _SHADING_BUF * sizeof(a_wWhiteShading[0]) );
2260 memset( a_wDarkShading, 0, _SHADING_BUF * sizeof(a_wDarkShading[0]) );
2261
2262 scan->skipCoarseCalib = SANE_FALSE;
2263
2264 if( dev->adj.cacheCalData )
2265 if( usb_ReadAndSetCalData( dev ))
2266 scan->skipCoarseCalib = SANE_TRUE;
2267
2268 /* as sheet-fed device we use the cached values, or
2269 * perform the calibration upon request
2270 */
2271 if( usb_IsSheetFedDevice(dev)) {
2272 if( !scan->skipCoarseCalib && !usb_InCalibrationMode(dev)) {
2273
2274 DBG(_DBG_INFO2,"SHEET-FED device, skip coarse calibration!\n");
2275 scan->skipCoarseCalib = SANE_TRUE;
2276
2277 regs[0x3b] = 0x0a;
2278 regs[0x3c] = 0x0a;
2279 regs[0x3d] = 0x0a;
2280
2281 /* use frontend values... */
2282 if((dev->adj.rofs != -1) &&
2283 (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
2284 regs[0x38] = (dev->adj.rofs & 0x3f);
2285 regs[0x39] = (dev->adj.gofs & 0x3f);
2286 regs[0x3a] = (dev->adj.bofs & 0x3f);
2287 }
2288
2289 if((dev->adj.rgain != -1) &&
2290 (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
2291 setAdjGain( dev->adj.rgain, ®s[0x3b] );
2292 setAdjGain( dev->adj.ggain, ®s[0x3c] );
2293 setAdjGain( dev->adj.bgain, ®s[0x3d] );
2294 }
2295 }
2296 }
2297 }
2298
2299 /**
2300 */
2301 static SANE_Bool
usb_SpeedTest(Plustek_Device * dev)2302 usb_SpeedTest( Plustek_Device *dev )
2303 {
2304 int i;
2305 double s, e, r, tr;
2306 struct timeval start, end;
2307 DCapsDef *scaps = &dev->usbDev.Caps;
2308 HWDef *hw = &dev->usbDev.HwSetting;
2309 u_char *regs = dev->usbDev.a_bRegs;
2310 u_long *scanbuf = dev->scanning.pScanBuffer;
2311
2312 if( usb_IsEscPressed())
2313 return SANE_FALSE;
2314
2315 bMaxITA = 0xff;
2316
2317 DBG( 1, "#########################\n" );
2318 DBG( 1, "usb_SpeedTest(%d,%lu)\n", dev->initialized, dev->transferRate );
2319 if( dev->transferRate != DEFAULT_RATE ) {
2320 DBG( 1, "* skipped, using already detected speed: %lu Bytes/s\n",
2321 dev->transferRate );
2322 return SANE_TRUE;
2323 }
2324
2325 usb_PrepareCalibration( dev );
2326 regs[0x38] = regs[0x39] = regs[0x3a] = 0;
2327 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2328
2329 /* define the strip to scan for warming up the lamp, in the end
2330 * we always scan the full line, even for TPA
2331 */
2332 m_ScanParam.bDataType = SCANDATATYPE_Color;
2333 m_ScanParam.bCalibration = PARAM_Gain;
2334 m_ScanParam.dMCLK = dMCLK;
2335 m_ScanParam.bBitDepth = 8;
2336 m_ScanParam.Size.dwLines = 1;
2337 m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
2338 scaps->OpticDpi.x / 300UL;
2339 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
2340 2 * m_ScanParam.bChannels;
2341
2342 if( usb_IsCISDevice(dev))
2343 m_ScanParam.Size.dwBytes *= 3;
2344
2345 m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
2346 300UL / scaps->OpticDpi.x);
2347 r = 0.0;
2348 dev->transferRate = 2000000;
2349
2350 for( i = 0; i < _TLOOPS ; i++ ) {
2351
2352 if( !usb_SetScanParameters( dev, &m_ScanParam ))
2353 return SANE_FALSE;
2354
2355 if( !usb_ScanBegin( dev, SANE_FALSE )) {
2356 DBG( _DBG_ERROR, "usb_SpeedTest() failed\n" );
2357 return SANE_FALSE;
2358 }
2359 if (!usb_IsDataAvailableInDRAM( dev ))
2360 return SANE_FALSE;
2361
2362 m_fFirst = SANE_FALSE;
2363 gettimeofday( &start, NULL );
2364 usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes );
2365 gettimeofday( &end, NULL );
2366 usb_ScanEnd( dev );
2367 s = (double)start.tv_sec * 1000000.0 + (double)start.tv_usec;
2368 e = (double)end.tv_sec * 1000000.0 + (double)end.tv_usec;
2369
2370 if( e > s )
2371 r += (e - s);
2372 else
2373 r += (s - e);
2374 }
2375
2376 tr = ((double)m_ScanParam.Size.dwPhyBytes * _TLOOPS * 1000000.0)/r;
2377 dev->transferRate = (u_long)tr;
2378 DBG( 1, "usb_SpeedTest() done - %u loops, %.4fus --> %.4f B/s, %lu\n",
2379 _TLOOPS, r, tr, dev->transferRate );
2380 return SANE_TRUE;
2381 }
2382
2383 /** read the white calibration strip until the lamp seems to be stable
2384 * the timed warmup will be used, when the warmup time is set to -1
2385 */
2386 static SANE_Bool
usb_AutoWarmup(Plustek_Device * dev)2387 usb_AutoWarmup( Plustek_Device *dev )
2388 {
2389 int i, stable_count;
2390 ScanDef *scanning = &dev->scanning;
2391 DCapsDef *scaps = &dev->usbDev.Caps;
2392 HWDef *hw = &dev->usbDev.HwSetting;
2393 u_long *scanbuf = scanning->pScanBuffer;
2394 u_char *regs = dev->usbDev.a_bRegs;
2395 u_long dw, start, end, len;
2396 u_long curR, curG, curB;
2397 u_long lastR, lastG, lastB;
2398 long diffR, diffG, diffB;
2399 long thresh = _AUTO_THRESH;
2400
2401 if( usb_IsEscPressed())
2402 return SANE_FALSE;
2403
2404 bMaxITA = 0xff;
2405
2406 DBG( _DBG_INFO, "#########################\n" );
2407 DBG( _DBG_INFO, "usb_AutoWarmup()\n" );
2408
2409 if( usb_IsCISDevice(dev)) {
2410 DBG( _DBG_INFO, "- function skipped, CIS device!\n" );
2411 return SANE_TRUE;
2412 }
2413
2414 if( dev->adj.warmup >= 0 ) {
2415 DBG( _DBG_INFO, "- using timed warmup: %ds\n", dev->adj.warmup );
2416 if( !usb_Wait4Warmup( dev )) {
2417 DBG( _DBG_ERROR, "- CANCEL detected\n" );
2418 return SANE_FALSE;
2419 }
2420 return SANE_TRUE;
2421 }
2422
2423 usb_PrepareCalibration( dev );
2424 regs[0x38] = regs[0x39] = regs[0x3a] = 0;
2425 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2426
2427 /* define the strip to scan for warming up the lamp, in the end
2428 * we always scan the full line, even for TPA
2429 */
2430 m_ScanParam.bDataType = SCANDATATYPE_Color;
2431 m_ScanParam.bCalibration = PARAM_Gain;
2432 m_ScanParam.dMCLK = dMCLK;
2433 m_ScanParam.Size.dwLines = 1;
2434 m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
2435 scaps->OpticDpi.x / 300UL;
2436 m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels *
2437 2 * m_ScanParam.bChannels;
2438
2439 if( usb_IsCISDevice(dev))
2440 m_ScanParam.Size.dwBytes *= 3;
2441
2442 m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
2443 300UL / scaps->OpticDpi.x);
2444
2445 stable_count = 0;
2446 start = 500;
2447 len = m_ScanParam.Size.dwPixels;
2448
2449 if( scanning->sParam.bSource == SOURCE_Transparency ) {
2450 start = scaps->Positive.DataOrigin.x * scaps->OpticDpi.x / 300UL;
2451 len = scaps->Positive.Size.x * scaps->OpticDpi.x / 300UL;
2452 thresh = _AUTO_TPA_THRESH;
2453 }
2454 else if( scanning->sParam.bSource == SOURCE_Negative ) {
2455 start = scaps->Negative.DataOrigin.x * scaps->OpticDpi.x / 300UL;
2456 len = scaps->Negative.Size.x * scaps->OpticDpi.x / 300UL;
2457 thresh = _AUTO_TPA_THRESH;
2458 }
2459 end = start + len;
2460 DBG( _DBG_INFO2, "Start=%lu, End=%lu, Len=%lu, Thresh=%li\n",
2461 start, end, len, thresh );
2462
2463 lastR = lastG = lastB = 0;
2464 for( i = 0; i < _MAX_AUTO_WARMUP + 1 ; i++ ) {
2465
2466 if( !usb_SetScanParameters( dev, &m_ScanParam ))
2467 return SANE_FALSE;
2468
2469 if( !usb_ScanBegin( dev, SANE_FALSE ) ||
2470 !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
2471 !usb_ScanEnd( dev )) {
2472 DBG( _DBG_ERROR, "usb_AutoWarmup() failed\n" );
2473 return SANE_FALSE;
2474 }
2475
2476 #ifdef SWAP_COARSE
2477 if(usb_HostSwap())
2478 #endif
2479 usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
2480
2481 if( end > m_ScanParam.Size.dwPhyPixels )
2482 end = m_ScanParam.Size.dwPhyPixels;
2483
2484 curR = curG = curB = 0;
2485 for( dw = start; dw < end; dw++ ) {
2486
2487 if( usb_IsCISDevice(dev)) {
2488 curR += ((u_short*)scanbuf)[dw];
2489 curG += ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
2490 curB += ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
2491 } else {
2492 curR += ((RGBUShortDef*)scanbuf)[dw].Red;
2493 curG += ((RGBUShortDef*)scanbuf)[dw].Green;
2494 curB += ((RGBUShortDef*)scanbuf)[dw].Blue;
2495 }
2496 }
2497 curR /= len;
2498 curG /= len;
2499 curB /= len;
2500
2501 diffR = curR - lastR; lastR = curR;
2502 diffG = curG - lastG; lastG = curG;
2503 diffB = curB - lastB; lastB = curB;
2504 DBG( _DBG_INFO2, "%i/%i-AVE(R,G,B)= %lu(%ld), %lu(%ld), %lu(%ld)\n",
2505 i, stable_count, curR, diffR, curG, diffG, curB, diffB );
2506
2507 /* we consider the lamp to be stable,
2508 * when the diffs are less than thresh for at least 3 loops
2509 */
2510 if((diffR < thresh) && (diffG < thresh) && (diffB < thresh)) {
2511 if( stable_count > 3 )
2512 break;
2513 stable_count++;
2514 } else {
2515 stable_count = 0;
2516 }
2517
2518 /* no need to sleep in the first loop */
2519 if((i != 0) && (stable_count == 0))
2520 sleep( _AUTO_SLEEP );
2521 }
2522
2523 DBG( _DBG_INFO, "usb_AutoWarmup() done - %u loops\n", i+1 );
2524 DBG( _DBG_INFO, "* AVE(R,G,B)= %lu(%ld), %lu(%ld), %lu(%ld)\n",
2525 curR, diffR, curG, diffG, curB, diffB );
2526 return SANE_TRUE;
2527 }
2528
2529 /**
2530 */
2531 static int
usb_DoIt(Plustek_Device * dev)2532 usb_DoIt( Plustek_Device *dev )
2533 {
2534 SANE_Bool skip_fine;
2535 ScanDef *scan = &dev->scanning;
2536
2537 DBG( _DBG_INFO, "Settings done, so start...\n" );
2538 if( !scan->skipCoarseCalib ) {
2539 DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" );
2540 if( !usb_AdjustGain(dev, 0)) {
2541 DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
2542 return _E_INTERNAL;
2543 }
2544 DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" );
2545 if( !usb_AdjustOffset(dev)) {
2546 DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
2547 return _E_INTERNAL;
2548 }
2549 } else {
2550 DBG( _DBG_INFO2, "Coarse Calibration skipped, using saved data\n" );
2551 }
2552
2553 skip_fine = SANE_FALSE;
2554 if( dev->adj.cacheCalData ) {
2555 skip_fine = usb_FineShadingFromFile(dev);
2556 }
2557
2558 if( !skip_fine ) {
2559 DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" );
2560 if( !usb_AdjustDarkShading(dev)) {
2561 DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
2562 return _E_INTERNAL;
2563 }
2564 DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" );
2565 if( !usb_AdjustWhiteShading(dev)) {
2566 DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
2567 return _E_INTERNAL;
2568 }
2569 } else {
2570 DBG( _DBG_INFO2, "###### FINE calibration skipped #######\n" );
2571
2572 m_ScanParam = scan->sParam;
2573 usb_GetPhyPixels( dev, &m_ScanParam );
2574
2575 usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
2576 m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
2577 usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
2578 m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
2579
2580 /* dev->usbDev.a_bRegs[0x45] &= ~0x10;*/
2581 }
2582 return 0;
2583 }
2584
2585 /** usb_DoCalibration
2586 */
2587 static int
usb_DoCalibration(Plustek_Device * dev)2588 usb_DoCalibration( Plustek_Device *dev )
2589 {
2590 int result;
2591 ScanDef *scanning = &dev->scanning;
2592 DCapsDef *scaps = &dev->usbDev.Caps;
2593 HWDef *hw = &dev->usbDev.HwSetting;
2594 u_char *regs = dev->usbDev.a_bRegs;
2595 double dRed, dGreen, dBlue;
2596
2597 DBG( _DBG_INFO, "usb_DoCalibration()\n" );
2598
2599 if( SANE_TRUE == scanning->fCalibrated )
2600 return SANE_TRUE;
2601
2602 /* Go to shading position
2603 */
2604 DBG( _DBG_INFO, "...goto shading position\n" );
2605
2606 /* HEINER: Currently not clear why Plustek didn't use the ShadingOriginY
2607 * for all modes
2608 * It should be okay to remove this and reference to the ShadingOriginY
2609 */
2610 #if 0
2611 if( scanning->sParam.bSource == SOURCE_Negative ) {
2612
2613 DBG( _DBG_INFO, "DataOrigin.x=%u, DataOrigin.y=%u\n",
2614 dev->usbDev.pSource->DataOrigin.x, dev->usbDev.pSource->DataOrigin.y);
2615 if(!usb_ModuleMove( dev, MOVE_Forward,
2616 (dev->usbDev.pSource->DataOrigin.y +
2617 dev->usbDev.pSource->Size.y / 2))) {
2618 return _E_LAMP_NOT_IN_POS;
2619 }
2620
2621 } else {
2622 #endif
2623 DBG( _DBG_INFO, "ShadingOriginY=%lu\n",
2624 (u_long)dev->usbDev.pSource->ShadingOriginY );
2625
2626 if((hw->motorModel == MODEL_HuaLien) && (scaps->OpticDpi.x==600)) {
2627 if (!usb_ModuleMove(dev, MOVE_ToShading,
2628 (u_long)dev->usbDev.pSource->ShadingOriginY)) {
2629 return _E_LAMP_NOT_IN_POS;
2630 }
2631 } else {
2632 if( !usb_ModuleMove(dev, MOVE_Forward,
2633 (u_long)dev->usbDev.pSource->ShadingOriginY)) {
2634 return _E_LAMP_NOT_IN_POS;
2635 }
2636 }
2637 /* }*/
2638
2639 DBG( _DBG_INFO, "shading position reached\n" );
2640
2641 usb_SpeedTest( dev );
2642
2643 if( !usb_AutoWarmup( dev ))
2644 return SANE_FALSE;
2645
2646 usb_PrepareCalibration( dev );
2647
2648 /** this won't work for Plustek devices!!!
2649 */
2650 #if 0
2651 if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ||
2652 !(SCANDEF_QualityScan & dev->scanning.dwFlag)) {
2653 #else
2654 if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
2655 #endif
2656
2657 DBG( _DBG_INFO, "--> BYPASS\n" );
2658 regs[0x38] = regs[0x39] = regs[0x3a] = 0;
2659 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2660
2661 setAdjGain( dev->adj.rgain, ®s[0x3b] );
2662 setAdjGain( dev->adj.ggain, ®s[0x3c] );
2663 setAdjGain( dev->adj.bgain, ®s[0x3d] );
2664
2665 regs[0x45] |= 0x10;
2666 usb_SetMCLK( dev, &scanning->sParam );
2667
2668 dumpregs( dev->fd, regs );
2669 DBG( _DBG_INFO, "<-- BYPASS\n" );
2670
2671 } else {
2672
2673 switch( scanning->sParam.bSource ) {
2674
2675 case SOURCE_Negative:
2676 DBG( _DBG_INFO, "NEGATIVE Shading\n" );
2677 m_dwIdealGain = IDEAL_GainNormal;
2678
2679 if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
2680 DBG( _DBG_INFO, "No Plustek model: %udpi\n",
2681 scanning->sParam.PhyDpi.x );
2682 usb_SetMCLK( dev, &scanning->sParam );
2683 } else {
2684
2685 if( dev->usbDev.Caps.OpticDpi.x == 600 )
2686 dMCLK = 7;
2687 else
2688 dMCLK = 8;
2689 }
2690
2691 for(;;) {
2692 if( usb_AdjustGain( dev, 2)) {
2693 if( regs[0x3b] && regs[0x3c] && regs[0x3d]) {
2694 break;
2695 } else {
2696 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2697 dMCLK--;
2698 }
2699 } else {
2700 return _E_LAMP_NOT_STABLE;
2701 }
2702 }
2703 scanning->sParam.dMCLK = dMCLK;
2704 Gain_Reg.Red = regs[0x3b];
2705 Gain_Reg.Green = regs[0x3c];
2706 Gain_Reg.Blue = regs[0x3d];
2707 Gain_NegHilight = Gain_Hilight;
2708
2709 DBG( _DBG_INFO, "MCLK = %.3f\n", dMCLK );
2710 DBG( _DBG_INFO, "GainRed = %u\n", regs[0x3b] );
2711 DBG( _DBG_INFO, "GainGreen = %u\n", regs[0x3c] );
2712 DBG( _DBG_INFO, "GainBlue = %u\n", regs[0x3d] );
2713
2714 #if 0
2715 if( !usb_ModuleMove( dev, MOVE_Backward,
2716 dev->usbDev.pSource->DataOrigin.y +
2717 dev->usbDev.pSource->Size.y / 2 -
2718 dev->usbDev.pSource->ShadingOriginY)) {
2719 return _E_LAMP_NOT_IN_POS;
2720 }
2721 #endif
2722 regs[0x45] &= ~0x10;
2723
2724 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2725
2726 if(!usb_AdjustGain( dev, 1 ))
2727 return _E_INTERNAL;
2728
2729 regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
2730
2731 DBG( _DBG_INFO, "Settings done, so start...\n" );
2732 if( !usb_AdjustOffset(dev) || !usb_AdjustDarkShading(dev) ||
2733 !usb_AdjustWhiteShading(dev)) {
2734 return _E_INTERNAL;
2735 }
2736
2737 dRed = 0.93 + 0.067 * Gain_Reg.Red;
2738 dGreen = 0.93 + 0.067 * Gain_Reg.Green;
2739 dBlue = 0.93 + 0.067 * Gain_Reg.Blue;
2740 dExpect = 2.85;
2741 if( dBlue >= dGreen && dBlue >= dRed )
2742 dMax = dBlue;
2743 else
2744 if( dGreen >= dRed && dGreen >= dBlue )
2745 dMax = dGreen;
2746 else
2747 dMax = dRed;
2748
2749 dMax = dExpect / dMax;
2750 dRed *= dMax;
2751 dGreen *= dMax;
2752 dBlue *= dMax;
2753
2754 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
2755 usb_ResizeWhiteShading( dRed, a_wWhiteShading,
2756 scanning->sParam.swGain[0]);
2757 usb_ResizeWhiteShading( dGreen, a_wWhiteShading +
2758 m_ScanParam.Size.dwPhyPixels,
2759 scanning->sParam.swGain[1]);
2760 usb_ResizeWhiteShading( dBlue, a_wWhiteShading +
2761 m_ScanParam.Size.dwPhyPixels*2,
2762 scanning->sParam.swGain[2]);
2763 }
2764 usb_line_statistics( "White", a_wWhiteShading,
2765 m_ScanParam.Size.dwPhyPixels, SANE_TRUE);
2766 break;
2767
2768 case SOURCE_ADF:
2769 DBG( _DBG_INFO, "ADF Shading\n" );
2770 m_dwIdealGain = IDEAL_GainPositive;
2771 if( scanning->sParam.bDataType == SCANDATATYPE_BW ) {
2772 if( scanning->sParam.PhyDpi.x <= 200 ) {
2773 scanning->sParam.dMCLK = 4.5;
2774 dMCLK = 4.0;
2775 } else if ( scanning->sParam.PhyDpi.x <= 300 ) {
2776 scanning->sParam.dMCLK = 4.0;
2777 dMCLK = 3.5;
2778 } else if( scanning->sParam.PhyDpi.x <= 400 ) {
2779 scanning->sParam.dMCLK = 5.0;
2780 dMCLK = 4.0;
2781 } else {
2782 scanning->sParam.dMCLK = 6.0;
2783 dMCLK = 4.0;
2784 }
2785 } else { /* Gray */
2786
2787 if( scanning->sParam.PhyDpi.x <= 400 ) {
2788 scanning->sParam.dMCLK = 6.0;
2789 dMCLK = 4.5;
2790 } else {
2791 scanning->sParam.dMCLK = 9.0;
2792 dMCLK = 7.0;
2793 }
2794 }
2795 dMCLK_ADF = dMCLK;
2796
2797 result = usb_DoIt( dev );
2798 if( result != 0 )
2799 return result;
2800 break;
2801
2802 case SOURCE_Transparency:
2803 DBG( _DBG_INFO, "TRANSPARENCY Shading\n" );
2804 m_dwIdealGain = IDEAL_GainPositive;
2805
2806 if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
2807 DBG( _DBG_INFO, "No Plustek model: %udpi\n",
2808 scanning->sParam.PhyDpi.x );
2809 usb_SetMCLK( dev, &scanning->sParam );
2810
2811 } else {
2812 if( dev->usbDev.Caps.OpticDpi.x == 600 ) {
2813 scanning->sParam.dMCLK = 8;
2814 dMCLK = 4;
2815 } else {
2816 scanning->sParam.dMCLK = 8;
2817 dMCLK = 6;
2818 }
2819 }
2820 result = usb_DoIt( dev );
2821 if( result != 0 )
2822 return result;
2823 break;
2824
2825 default:
2826 if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
2827 DBG( _DBG_INFO, "No Plustek model: %udpi\n",
2828 scanning->sParam.PhyDpi.x );
2829 usb_SetMCLK( dev, &scanning->sParam );
2830
2831 } else if( dev->usbDev.Caps.OpticDpi.x == 600 ) {
2832
2833 DBG( _DBG_INFO, "Default Shading (600dpi)\n" );
2834
2835 if( dev->usbDev.Caps.bCCD == kSONY548 ) {
2836
2837 DBG( _DBG_INFO, "CCD - SONY548\n" );
2838 if( scanning->sParam.PhyDpi.x <= 75 ) {
2839 if( scanning->sParam.bDataType == SCANDATATYPE_Color )
2840 scanning->sParam.dMCLK = dMCLK = 2.5;
2841 else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
2842 scanning->sParam.dMCLK = dMCLK = 7.0;
2843 else
2844 scanning->sParam.dMCLK = dMCLK = 7.0;
2845
2846 } else if( scanning->sParam.PhyDpi.x <= 300 ) {
2847 if( scanning->sParam.bDataType == SCANDATATYPE_Color )
2848 scanning->sParam.dMCLK = dMCLK = 3.0;
2849 else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
2850 scanning->sParam.dMCLK = dMCLK = 6.0;
2851 else {
2852 if( scanning->sParam.PhyDpi.x <= 100 )
2853 scanning->sParam.dMCLK = dMCLK = 6.0;
2854 else if( scanning->sParam.PhyDpi.x <= 200 )
2855 scanning->sParam.dMCLK = dMCLK = 5.0;
2856 else
2857 scanning->sParam.dMCLK = dMCLK = 4.5;
2858 }
2859 } else if( scanning->sParam.PhyDpi.x <= 400 ) {
2860 if( scanning->sParam.bDataType == SCANDATATYPE_Color )
2861 scanning->sParam.dMCLK = dMCLK = 4.0;
2862 else if( scanning->sParam.bDataType == SCANDATATYPE_Gray )
2863 scanning->sParam.dMCLK = dMCLK = 6.0;
2864 else
2865 scanning->sParam.dMCLK = dMCLK = 4.0;
2866 } else {
2867 if(scanning->sParam.bDataType == SCANDATATYPE_Color)
2868 scanning->sParam.dMCLK = dMCLK = 6.0;
2869 else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
2870 scanning->sParam.dMCLK = dMCLK = 7.0;
2871 else
2872 scanning->sParam.dMCLK = dMCLK = 6.0;
2873 }
2874
2875 } else if( dev->usbDev.Caps.bPCB == 0x02 ) {
2876 DBG( _DBG_INFO, "PCB - 0x02\n" );
2877 if( scanning->sParam.PhyDpi.x > 300 )
2878 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 16);
2879 else if( scanning->sParam.PhyDpi.x > 150 )
2880 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?4.5: 13.5);
2881 else
2882 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 8);
2883
2884 } else if( dev->usbDev.Caps.bButtons ) { /* with lens Shading piece (with gobo) */
2885
2886 DBG( _DBG_INFO, "CAPS - Buttons\n" );
2887 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
2888 if( dev->usbDev.HwSetting.motorModel == MODEL_KaoHsiung ) {
2889 if( dev->usbDev.Caps.bCCD == kNEC3799 ) {
2890 if( scanning->sParam.PhyDpi.x > 300 )
2891 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 13);
2892 else if(scanning->sParam.PhyDpi.x > 150)
2893 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?4.5:13.5);
2894 else
2895 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
2896 } else {
2897 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
2898 }
2899 } else { /* motorModel == MODEL_Hualien */
2900 /* IMPORTANT !!!!
2901 * for Hualien 600 dpi scanner big noise
2902 */
2903 hw->wLineEnd = 5384;
2904 if(scanning->sParam.bDataType == SCANDATATYPE_Color &&
2905 ((scanning->sParam.bBitDepth == 8 &&
2906 (scanning->sParam.PhyDpi.x == 200 ||scanning->sParam.PhyDpi.x == 300))))
2907 hw->wLineEnd = 7000;
2908 regs[0x20] = _HIBYTE(hw->wLineEnd);
2909 regs[0x21] = _LOBYTE(hw->wLineEnd);
2910
2911 if( scanning->sParam.PhyDpi.x > 300 ) {
2912 if (scanning->sParam.bBitDepth > 8)
2913 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 5: 13);
2914 else
2915 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 13);
2916 } else {
2917 if( scanning->sParam.bBitDepth > 8 )
2918 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 5: 13);
2919 else
2920 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
2921 }
2922 }
2923 } else { /* without lens Shading piece (without gobo) - Model U12 only */
2924 DBG( _DBG_INFO, "Default trunc (U12)\n" );
2925 if( scanning->sParam.PhyDpi.x > 300 )
2926 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 3: 9);
2927 else
2928 scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 2: 6);
2929 }
2930 } else { /* Device.Caps.OpticDpi.x == 1200 */
2931
2932 DBG( _DBG_INFO, "Default Shading (1200dpi)\n" );
2933 if( scanning->sParam.bDataType != SCANDATATYPE_Color ) {
2934 if( scanning->sParam.PhyDpi.x > 300 )
2935 scanning->sParam.dMCLK = dMCLK = 6.0;
2936 else {
2937 scanning->sParam.dMCLK = dMCLK = 5.0;
2938 regs[0x0a] = 1;
2939 }
2940 } else {
2941 if( scanning->sParam.PhyDpi.x <= 300)
2942 scanning->sParam.dMCLK = dMCLK = 2.0;
2943 else if( scanning->sParam.PhyDpi.x <= 800 )
2944 scanning->sParam.dMCLK = dMCLK = 4.0;
2945 else
2946 scanning->sParam.dMCLK = dMCLK = 5.5;
2947 }
2948 }
2949
2950 if (m_ScanParam.bSource == SOURCE_ADF)
2951 m_dwIdealGain = IDEAL_GainPositive;
2952 else
2953 m_dwIdealGain = IDEAL_GainNormal;
2954
2955 result = usb_DoIt( dev );
2956 if( result != 0 )
2957 return result;
2958 break;
2959 }
2960 }
2961
2962 /* home the sensor after calibration */
2963 if( _IS_PLUSTEKMOTOR(hw->motorModel)) {
2964 if( hw->motorModel != MODEL_Tokyo600 ) {
2965 usb_ModuleMove ( dev, MOVE_Forward, hw->wMotorDpi / 5 );
2966 usb_ModuleToHome( dev, SANE_TRUE );
2967 }
2968 } else {
2969 usb_ModuleMove( dev, MOVE_Forward, 10 );
2970 usleep( 1500 );
2971 usb_ModuleToHome( dev, SANE_TRUE );
2972 }
2973
2974 if( scanning->sParam.bSource == SOURCE_ADF ) {
2975
2976 if( scaps->bCCD == kNEC3778 )
2977 usb_ModuleMove( dev, MOVE_Forward, 1000 );
2978
2979 else /* if( scaps->bCCD == kNEC3799) */
2980 usb_ModuleMove( dev, MOVE_Forward, 3 * 300 + 38 );
2981
2982 usb_MotorOn( dev, SANE_FALSE );
2983 }
2984
2985 scanning->fCalibrated = SANE_TRUE;
2986 DBG( _DBG_INFO, "Calibration done\n" );
2987 DBG( _DBG_INFO, "-----------------------\n" );
2988 DBG( _DBG_INFO, "Static Gain:\n" );
2989 DBG( _DBG_INFO, "REG[0x3b] = %u\n", regs[0x3b] );
2990 DBG( _DBG_INFO, "REG[0x3c] = %u\n", regs[0x3c] );
2991 DBG( _DBG_INFO, "REG[0x3d] = %u\n", regs[0x3d] );
2992 DBG( _DBG_INFO, "Static Offset:\n" );
2993 DBG( _DBG_INFO, "REG[0x38] = %i\n", regs[0x38] );
2994 DBG( _DBG_INFO, "REG[0x39] = %i\n", regs[0x39] );
2995 DBG( _DBG_INFO, "REG[0x3a] = %i\n", regs[0x3a] );
2996 DBG( _DBG_INFO, "MCLK = %.2f\n", scanning->sParam.dMCLK );
2997 DBG( _DBG_INFO, "-----------------------\n" );
2998 return SANE_TRUE;
2999 }
3000
3001 /* on different sensor orders, we need to adjust the shading buffer
3002 * pointer, otherwise we correct the wrong channels
3003 */
3004 static void
3005 get_ptrs(Plustek_Device *dev, u_short *buf, u_long offs,
3006 u_short **r, u_short **g, u_short **b)
3007 {
3008 ScanDef *scan = &dev->scanning;
3009 DCapsDef *scaps = &dev->usbDev.Caps;
3010 u_char so = scaps->bSensorOrder;
3011
3012 if (_WAF_RESET_SO_TO_RGB & scaps->workaroundFlag) {
3013 if (scaps->bPCB != 0) {
3014 if (scan->sParam.PhyDpi.x > scaps->bPCB)
3015 so = SENSORORDER_rgb;
3016 }
3017 }
3018
3019 switch( so ) {
3020 case SENSORORDER_gbr:
3021 *g = buf;
3022 *b = buf + offs;
3023 *r = buf + offs * 2;
3024 break;
3025
3026 case SENSORORDER_bgr:
3027 *b = buf;
3028 *g = buf + offs;
3029 *r = buf + offs * 2;
3030 break;
3031
3032 case SENSORORDER_rgb:
3033 default:
3034 *r = buf;
3035 *g = buf + offs;
3036 *b = buf + offs * 2;
3037 break;
3038 }
3039 }
3040
3041 /** usb_DownloadShadingData
3042 * according to the job id, different registers or DRAM areas are set
3043 * in preparation for calibration or scanning
3044 */
3045 static SANE_Bool
3046 usb_DownloadShadingData( Plustek_Device *dev, u_char what )
3047 {
3048 u_char channel;
3049 u_short *r, *g, *b;
3050 DCapsDef *scaps = &dev->usbDev.Caps;
3051 ScanDef *scan = &dev->scanning;
3052 HWDef *hw = &dev->usbDev.HwSetting;
3053 ScanParam *param = &dev->scanning.sParam;
3054 u_char *regs = dev->usbDev.a_bRegs;
3055
3056 DBG( _DBG_INFO, "usb_DownloadShadingData(%u)\n", what );
3057
3058 channel = CHANNEL_green;
3059 if( usb_IsCISDevice(dev))
3060 channel = CHANNEL_blue;
3061
3062 switch( what ) {
3063
3064 case PARAM_WhiteShading:
3065 if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
3066
3067 usb_SetDarkShading( dev, CHANNEL_red, a_wDarkShading,
3068 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3069 usb_SetDarkShading( dev, CHANNEL_green, a_wDarkShading +
3070 m_ScanParam.Size.dwPhyPixels,
3071 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3072 usb_SetDarkShading( dev, CHANNEL_blue, a_wDarkShading +
3073 m_ScanParam.Size.dwPhyPixels * 2,
3074 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3075 } else {
3076 usb_SetDarkShading( dev, channel, a_wDarkShading +
3077 m_ScanParam.Size.dwPhyPixels,
3078 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3079 }
3080 regs[0x40] = 0x40;
3081 regs[0x41] = 0x00;
3082
3083 /* set RAM configuration AND
3084 * Gain = Multiplier Coefficient/16384
3085 * CFG Register 0x40/0x41 for Multiplier Coefficient Source
3086 * External DRAM for Offset Coefficient Source
3087 */
3088 regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x64: 0x24);
3089 _UIO(sanei_lm983x_write( dev->fd, 0x40,
3090 ®s[0x40], 0x42-0x40+1, SANE_TRUE ));
3091 break;
3092
3093 case PARAM_Scan:
3094 {
3095 #if 0
3096 if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ||
3097 !(SCANDEF_QualityScan & dev->scanning.dwFlag)) {
3098 #else
3099 if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
3100 #endif
3101 DBG( _DBG_INFO, "--> BYPASS\n" );
3102 /* set RAM configuration AND
3103 * Bypass Multiplier
3104 * CFG Register 0x40/0x41 for Multiplier Coefficient Source
3105 * CFG Register 0x3e/0x3f for Offset Coefficient Source
3106 */
3107 regs[0x03] = 0;
3108 regs[0x40] = 0x40;
3109 regs[0x41] = 0x00;
3110 regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x61:0x21);
3111 if( !usbio_WriteReg( dev->fd, 0x03, regs[0x03]))
3112 return SANE_FALSE;
3113
3114 _UIO(sanei_lm983x_write( dev->fd, 0x40,
3115 ®s[0x40], 3, SANE_TRUE));
3116 break;
3117 }
3118
3119 if( _LM9831 != hw->chip )
3120 m_dwPixels = m_ScanParam.Size.dwPhyPixels;
3121
3122 if( scaps->workaroundFlag & _WAF_SKIP_FINE ) {
3123 DBG( _DBG_INFO, "Skipping fine calibration\n" );
3124 regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x60: 0x20);
3125 if (scan->skipCoarseCalib) {
3126
3127 DBG( _DBG_INFO, "...cleaning shading buffer\n" );
3128 memset( a_wWhiteShading, 0, _SHADING_BUF * sizeof(a_wWhiteShading[0]) );
3129 memset( a_wDarkShading, 0, _SHADING_BUF * sizeof(a_wDarkShading[0]) );
3130
3131 regs[0x40] = 0x3f;
3132 regs[0x41] = 0xff;
3133
3134 _UIO(sanei_lm983x_write( dev->fd, 0x40,
3135 ®s[0x40], 3, SANE_TRUE));
3136 } else {
3137 if( !usbio_WriteReg( dev->fd, 0x42, regs[0x42]))
3138 return SANE_FALSE;
3139 }
3140 break;
3141 }
3142
3143 DBG( _DBG_INFO, "Downloading %lu pixels\n", m_dwPixels );
3144 /* Download the dark & white shadings to LM983x */
3145 if( param->bDataType == SCANDATATYPE_Color ) {
3146
3147 get_ptrs(dev, a_wDarkShading, m_dwPixels, &r, &g, &b);
3148
3149 usb_SetDarkShading( dev, CHANNEL_red, r,
3150 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3151 usb_SetDarkShading( dev, CHANNEL_green, g,
3152 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3153 usb_SetDarkShading( dev, CHANNEL_blue, b,
3154 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3155 } else {
3156 usb_SetDarkShading( dev, channel,
3157 a_wDarkShading + m_dwPixels,
3158 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3159 }
3160 if( param->bDataType == SCANDATATYPE_Color ) {
3161
3162 get_ptrs(dev, a_wWhiteShading,
3163 m_ScanParam.Size.dwPhyPixels, &r, &g, &b);
3164
3165 usb_SetWhiteShading( dev, CHANNEL_red, r,
3166 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3167 usb_SetWhiteShading( dev, CHANNEL_green, g,
3168 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3169 usb_SetWhiteShading( dev, CHANNEL_blue, b,
3170 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3171 } else {
3172 usb_SetWhiteShading( dev, channel, a_wWhiteShading,
3173 (u_short)m_ScanParam.Size.dwPhyPixels * 2);
3174 }
3175
3176 /* set RAM configuration AND
3177 * Gain = Multiplier Coefficient/16384
3178 * External DRAM for Multiplier Coefficient Source
3179 * External DRAM for Offset Coefficient Source
3180 */
3181 regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x66: 0x26);
3182
3183 if( scaps->workaroundFlag & _WAF_SKIP_WHITEFINE ) {
3184 DBG( _DBG_INFO,"Skipping fine white calibration result\n");
3185 regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x64: 0x24);
3186 }
3187
3188 if( !usbio_WriteReg( dev->fd, 0x42, regs[0x42]))
3189 return SANE_FALSE;
3190 }
3191 break;
3192
3193 default:
3194 /* for coarse calibration and "black fine" */
3195 regs[0x3e] = 0;
3196 regs[0x3f] = 0;
3197 regs[0x40] = 0x40;
3198 regs[0x41] = 0x00;
3199
3200 /* set RAM configuration AND
3201 * GAIN = Multiplier Coefficient/16384
3202 * CFG Register 0x40/0x41 for Multiplier Coefficient Source
3203 * CFG Register 0x3e/0x3f for Offset Coefficient Source
3204 */
3205 regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x60: 0x20);
3206 _UIO(sanei_lm983x_write( dev->fd, 0x3e, ®s[0x3e],
3207 0x42 - 0x3e + 1, SANE_TRUE ));
3208 break;
3209 }
3210 return SANE_TRUE;
3211 }
3212
3213 /* END PLUSTEK-USBSHADING.C .................................................*/
3214