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, &regs[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, &regs[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, &regs[0x3b] );
800 		setAdjGain( dev->adj.ggain, &regs[0x3c] );
801 		setAdjGain( dev->adj.bgain, &regs[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, &regs[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, &regs[0x3b] );
2292 				setAdjGain( dev->adj.ggain, &regs[0x3c] );
2293 				setAdjGain( dev->adj.bgain, &regs[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, &regs[0x3b] );
2662 		setAdjGain( dev->adj.ggain, &regs[0x3c] );
2663 		setAdjGain( dev->adj.bgain, &regs[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 			                        &regs[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 					                        &regs[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 						                        &regs[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, &regs[0x3e],
3207 			                         0x42 - 0x3e + 1, SANE_TRUE ));
3208 			break;
3209 	}
3210 	return SANE_TRUE;
3211 }
3212 
3213 /* END PLUSTEK-USBSHADING.C .................................................*/
3214