kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			3216 wiersze
		
	
	
		
			92 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			3216 wiersze
		
	
	
		
			92 KiB
		
	
	
	
		
			C
		
	
	
| /*.............................................................................
 | |
|  * Project : SANE library for Plustek flatbed scanners.
 | |
|  *.............................................................................
 | |
|  */
 | |
| 
 | |
| /** @file plustek-usbshading.c
 | |
|  *  @brief Calibration routines.
 | |
|  *
 | |
|  * Based on sources acquired from Plustek Inc.<br>
 | |
|  * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
 | |
|  *
 | |
|  * History:
 | |
|  * - 0.40 - starting version of the USB support
 | |
|  * - 0.41 - minor fixes
 | |
|  *        - added workaround stuff for EPSON1250
 | |
|  * - 0.42 - added workaround stuff for UMAX 3400
 | |
|  * - 0.43 - added call to usb_switchLamp before reading dark data
 | |
|  * - 0.44 - added more debug output and bypass calibration
 | |
|  *        - added dump of shading data
 | |
|  * - 0.45 - added coarse calibration for CIS devices
 | |
|  *        - added _WAF_SKIP_FINE to skip the results of fine calibration
 | |
|  *        - CanoScan fixes and fine-tuning
 | |
|  * - 0.46 - CanoScan will now be calibrated by code in plustek-usbcal.c
 | |
|  *        - added functions to save and restore calibration data from a file
 | |
|  *        - fixed some TPA issues
 | |
|  * - 0.47 - made calibration work on big-endian machines
 | |
|  * - 0.48 - added more debug output
 | |
|  *        - added usb_AutoWarmup()
 | |
|  * - 0.49 - a_bRegs is now part of the device structure
 | |
|  *        - using now PhyDpi.y as selector for the motor MCLK range
 | |
|  * - 0.50 - readded kCIS670 to add 5% extra to LiDE20 fine calibration
 | |
|  *        - fixed line statistics and added data output
 | |
|  * - 0.51 - added fine calibration cache
 | |
|  * - 0.52 - added get_ptrs to let various sensororders work
 | |
|  *          correctly
 | |
|  *        - fixed warning condition
 | |
|  * .
 | |
|  * <hr>
 | |
|  * This file is part of the SANE package.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License as
 | |
|  * published by the Free Software Foundation; either version 2 of the
 | |
|  * License, or (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|  * MA 02111-1307, USA.
 | |
|  *
 | |
|  * As a special exception, the authors of SANE give permission for
 | |
|  * additional uses of the libraries contained in this release of SANE.
 | |
|  *
 | |
|  * The exception is that, if you link a SANE library with other files
 | |
|  * to produce an executable, this does not by itself cause the
 | |
|  * resulting executable to be covered by the GNU General Public
 | |
|  * License.  Your use of that executable is in no way restricted on
 | |
|  * account of linking the SANE library code into it.
 | |
|  *
 | |
|  * This exception does not, however, invalidate any other reasons why
 | |
|  * the executable file might be covered by the GNU General Public
 | |
|  * License.
 | |
|  *
 | |
|  * If you submit changes to SANE to the maintainers to be included in
 | |
|  * a subsequent release, you agree by submitting the changes that
 | |
|  * those changes may be distributed with this exception intact.
 | |
|  *
 | |
|  * If you write modifications of your own for SANE, it is your choice
 | |
|  * whether to permit this exception to apply to your modifications.
 | |
|  * If you do not wish that, delete this exception notice.
 | |
|  * <hr>
 | |
|  */
 | |
| 
 | |
| /************** global stuff - I hate it, but we need it... ******************/
 | |
| 
 | |
| #define _MAX_AUTO_WARMUP 60  /**< number of loops                            */
 | |
| #define _AUTO_SLEEP       1  /**< time to sleep, when lamp is not stable     */
 | |
| #define _AUTO_THRESH     60  /**< threshold for stop waiting (normal lamp)   */
 | |
| #define _AUTO_TPA_THRESH 40  /**< threshold for stop waiting (TPA lamp)      */
 | |
| 
 | |
| #define _MAX_GAIN_LOOPS  10  /**< max number of loops for coarse calibration */
 | |
| #define _TLOOPS           3  /**< test loops for transfer rate measurement   */
 | |
| 
 | |
| #define SWAP_COARSE
 | |
| #define SWAP_FINE
 | |
| 
 | |
| /************************** static variables *********************************/
 | |
| 
 | |
| static RGBUShortDef Gain_Hilight;
 | |
| static RGBUShortDef Gain_NegHilight;
 | |
| static RGBByteDef   Gain_Reg;
 | |
| 
 | |
| static u_long     m_dwPixels;
 | |
| static ScanParam  m_ScanParam;
 | |
| static u_long     m_dwIdealGain;
 | |
| 
 | |
| static double dMCLK, dExpect, dMax;
 | |
| static double dMCLK_ADF;
 | |
| 
 | |
| /** do some statistics...
 | |
|  */
 | |
| static void usb_line_statistics( char *cmt, u_short* buf,
 | |
|                                  u_long dim_x, SANE_Bool color )
 | |
| {
 | |
| 	char         fn[50];
 | |
| 	int          i, channel;
 | |
| 	u_long       dw, imad, imid, alld, cld, cud;
 | |
| 	u_short      mid, mad, aved, lbd, ubd, tmp;
 | |
| 	MonoWordDef *pvd, *pvd2;
 | |
| 	FILE        *fp;
 | |
| 	SANE_Bool    swap = usb_HostSwap();
 | |
| 
 | |
| 	pvd = pvd2 = (MonoWordDef*)buf;
 | |
| 
 | |
| 	if( color )
 | |
| 		channel = 3;
 | |
| 	else
 | |
| 		channel = 1;
 | |
| 
 | |
| 	for( i = 0; i < channel; i++ ) {
 | |
| 
 | |
| 		mid  = 0xFFFF;
 | |
| 		mad  = 0;
 | |
| 		imid = 0;
 | |
| 		imad = 0;
 | |
| 		alld = 0;
 | |
| 
 | |
| 		fp = NULL;
 | |
| 		if( DBG_LEVEL >= _DBG_DCALDATA ) {
 | |
| 			sprintf( fn, "%scal%u.dat", cmt, i );
 | |
| 			fp = fopen( fn, "w+b" );
 | |
| 			if( fp == NULL )
 | |
| 				DBG( _DBG_ERROR, "Could not open %s\n", fn );
 | |
| 		}
 | |
| 
 | |
| 		/* do the standard min/max stuff */
 | |
| 		for( dw = 0; dw < dim_x; pvd++, dw++ ) {
 | |
| 
 | |
| 			if( swap )
 | |
| 				tmp = _LOBYTE(pvd->Mono) * 256 + _HIBYTE(pvd->Mono);
 | |
| 			else
 | |
| 				tmp = pvd->Mono;
 | |
| 
 | |
| 			if( tmp > mad ) {
 | |
| 				mad  = tmp;
 | |
| 				imad = dw;
 | |
| 			}
 | |
| 
 | |
| 			if( tmp < mid ) {
 | |
| 				mid  = tmp;
 | |
| 				imid = dw;
 | |
| 			}
 | |
| 
 | |
| 			if( fp )
 | |
| 				fprintf(fp, "%u\n", tmp );
 | |
| 
 | |
| 			alld += tmp;
 | |
| 		}
 | |
| 
 | |
| 		if( fp )
 | |
| 			fclose(fp);
 | |
| 
 | |
| 		/* calculate average and 5% limit */
 | |
| 		aved = (u_short)(alld/dim_x);
 | |
| 		lbd  = aved - 0.05*aved;
 | |
| 		ubd  = aved + 0.05*aved;
 | |
| 		cld  = 0;
 | |
| 		cud  = 0;
 | |
| 
 | |
| 		/* find the number of values beyond the 5% limits */
 | |
| 		for( dw = 0; dw < dim_x; pvd2++, dw++ ) {
 | |
| 
 | |
| 			if( swap )
 | |
| 				tmp = _LOBYTE(pvd2->Mono) * 256 + _HIBYTE(pvd2->Mono);
 | |
| 			else
 | |
| 				tmp = pvd2->Mono;
 | |
| 
 | |
| 			if( tmp > ubd ) cud++;
 | |
| 			if( tmp < lbd ) cld++;
 | |
| 		}
 | |
| 
 | |
| 		DBG( _DBG_INFO2, "Color[%u] (%s): %lu all "
 | |
| 		                 "min=%u(%lu) max=%u(%lu) ave=%u\n",
 | |
| 		                 i, cmt, dim_x, mid, imid, mad, imad, aved);
 | |
| 		DBG( _DBG_INFO2, "5%%: low@%u (count=%lu), upper@%u (count=%lu)\n",
 | |
| 		                 lbd, cld, ubd, cud);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  */
 | |
| static void
 | |
| usb_PrepareFineCal( Plustek_Device *dev, ScanParam *tmp_sp, u_short cal_dpi )
 | |
| {
 | |
| 	ScanParam *sp    = &dev->scanning.sParam;
 | |
| 	DCapsDef  *scaps = &dev->usbDev.Caps;
 | |
| 
 | |
| 	*tmp_sp = *sp;
 | |
| 
 | |
| 	if( dev->adj.cacheCalData ) {
 | |
| 
 | |
| 		DBG( _DBG_INFO2, "* Cal-cache active, tweaking scanparams"
 | |
| 		                 " - DPI=%u!\n", cal_dpi );
 | |
| 
 | |
| 		tmp_sp->UserDpi.x = usb_SetAsicDpiX(dev, sp->UserDpi.x );
 | |
| 		if( cal_dpi != 0 )
 | |
| 			tmp_sp->UserDpi.x = cal_dpi;
 | |
| 
 | |
| 		tmp_sp->PhyDpi        = scaps->OpticDpi;
 | |
| 		tmp_sp->Origin.x      = 0;
 | |
| 		tmp_sp->Size.dwPixels = scaps->Normal.Size.x *
 | |
| 		                        usb_SetAsicDpiX(dev, tmp_sp->UserDpi.x)/ 300UL;
 | |
| 	}
 | |
| 
 | |
| #if 0
 | |
| 	if( tmp_sp->PhyDpi.x > 75)
 | |
| 		tmp_sp->Size.dwLines = 64;
 | |
| 	else
 | |
| #endif
 | |
| 		tmp_sp->Size.dwLines = 32;
 | |
| 
 | |
| 	tmp_sp->Origin.y  = 0;
 | |
| 	tmp_sp->bBitDepth = 16;
 | |
| 
 | |
| 	tmp_sp->UserDpi.y    = scaps->OpticDpi.y;
 | |
| 	tmp_sp->Size.dwBytes = tmp_sp->Size.dwPixels * 2 * tmp_sp->bChannels;
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev) && (tmp_sp->bDataType == SCANDATATYPE_Color)) {
 | |
| 		tmp_sp->Size.dwBytes *= 3;
 | |
| 	}
 | |
| 
 | |
| 	tmp_sp->dMCLK = dMCLK;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  */
 | |
| static double usb_GetMCLK( Plustek_Device *dev, ScanParam *param )
 | |
| {
 | |
| 	int          idx, i;
 | |
| 	double       mclk;
 | |
| 	ClkMotorDef *clk;
 | |
| 	HWDef       *hw = &dev->usbDev.HwSetting;
 | |
| 
 | |
| 	clk = usb_GetMotorSet( hw->motorModel );
 | |
| 	idx = 0;
 | |
| 	for( i = 0; i < _MAX_CLK; i++ ) {
 | |
| 		if( param->PhyDpi.y <= dpi_ranges[i] )
 | |
| 			break;
 | |
| 		idx++;
 | |
| 	}
 | |
| 	if( idx >= _MAX_CLK )
 | |
| 		idx = _MAX_CLK - 1;
 | |
| 
 | |
| 	if( param->bDataType != SCANDATATYPE_Color ) {
 | |
| 
 | |
| 		if( param->bBitDepth > 8 )
 | |
| 			mclk = clk->gray_mclk_16[idx];
 | |
| 		else
 | |
| 			mclk = clk->gray_mclk_8[idx];
 | |
| 	} else {
 | |
| 		if( param->bBitDepth > 8 )
 | |
| 			mclk = clk->color_mclk_16[idx];
 | |
| 		else
 | |
| 			mclk = clk->color_mclk_8[idx];
 | |
| 	}
 | |
| 
 | |
| 	DBG( _DBG_INFO, "GETMCLK[%u/%u], using entry %u: %.3f, %u\n",
 | |
| 	     hw->motorModel, param->bDataType, idx, mclk, param->PhyDpi.x );
 | |
| 	return mclk;
 | |
| }
 | |
| 
 | |
| /** usb_SetMCLK
 | |
|  * get the MCLK out of our table
 | |
|  */
 | |
| static void usb_SetMCLK( Plustek_Device *dev, ScanParam *param )
 | |
| {
 | |
| 	HWDef *hw = &dev->usbDev.HwSetting;
 | |
| 
 | |
| 	dMCLK = usb_GetMCLK( dev, param );
 | |
| 	param->dMCLK = dMCLK; 
 | |
| 
 | |
| 	DBG( _DBG_INFO, "SETMCLK[%u/%u]: %.3f\n", 
 | |
| 	     hw->motorModel, param->bDataType, dMCLK );
 | |
| }
 | |
| 
 | |
| /** usb_SetDarkShading
 | |
|  * download the dark shading data to Merlins' DRAM
 | |
|  */
 | |
| static SANE_Bool usb_SetDarkShading( Plustek_Device *dev, u_char channel,
 | |
|                                      void *coeff_buffer, u_short wCount )
 | |
| {
 | |
| 	int     res;
 | |
| 	u_char *regs = dev->usbDev.a_bRegs;
 | |
| 
 | |
| 	regs[0x03] = 0;
 | |
| 	if( channel == CHANNEL_green )
 | |
| 		regs[0x03] |= 4;
 | |
| 	else
 | |
| 		if( channel == CHANNEL_blue )
 | |
| 			regs[0x03] |= 8;
 | |
| 
 | |
| 	if( usbio_WriteReg( dev->fd, 0x03, regs[0x03] )) {
 | |
| 
 | |
| 		/* Dataport address is always 0 for setting offset coefficient */
 | |
| 		regs[0x04] = 0;
 | |
| 		regs[0x05] = 0;
 | |
| 
 | |
| 		res = sanei_lm983x_write( dev->fd, 0x04, ®s[0x04], 2, SANE_TRUE );
 | |
| 
 | |
| 		/* Download offset coefficients */
 | |
| 		if( SANE_STATUS_GOOD == res ) {
 | |
| 			
 | |
| 			res = sanei_lm983x_write( dev->fd, 0x06,
 | |
| 			                          (u_char*)coeff_buffer, wCount, SANE_FALSE );
 | |
| 			if( SANE_STATUS_GOOD == res )
 | |
| 				return SANE_TRUE;
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	DBG( _DBG_ERROR, "usb_SetDarkShading() failed\n" );
 | |
| 	return SANE_FALSE;
 | |
| }
 | |
| 
 | |
| /** usb_SetWhiteShading
 | |
|  * download the white shading data to Merlins' DRAM
 | |
|  */
 | |
| static SANE_Bool usb_SetWhiteShading( Plustek_Device *dev, u_char channel,
 | |
|                                       void *data_buffer, u_short wCount )
 | |
| {
 | |
| 	int     res;
 | |
| 	u_char *regs = dev->usbDev.a_bRegs;
 | |
| 
 | |
| 	regs[0x03] = 1;
 | |
| 	if (channel == CHANNEL_green)
 | |
| 		regs [0x03] |= 4;
 | |
| 	else
 | |
| 		if (channel == CHANNEL_blue)
 | |
| 			regs[0x03] |= 8;
 | |
| 
 | |
| 	if( usbio_WriteReg( dev->fd, 0x03, regs[0x03] )) {
 | |
| 
 | |
| 		/* Dataport address is always 0 for setting offset coefficient */
 | |
| 		regs[0x04] = 0;
 | |
| 		regs[0x05] = 0;
 | |
| 
 | |
| 		res = sanei_lm983x_write( dev->fd, 0x04, ®s[0x04], 2, SANE_TRUE );
 | |
| 
 | |
| 		/* Download offset coefficients */
 | |
| 		if( SANE_STATUS_GOOD == res ) {
 | |
| 			res = sanei_lm983x_write(dev->fd, 0x06,
 | |
| 			                        (u_char*)data_buffer, wCount, SANE_FALSE );
 | |
| 			if( SANE_STATUS_GOOD == res )
 | |
| 				return SANE_TRUE;
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	DBG( _DBG_ERROR, "usb_SetWhiteShading() failed\n" );
 | |
| 	return SANE_FALSE;
 | |
| }
 | |
| 
 | |
| /** usb_GetSWOffsetGain4TPA
 | |
|  *  preset the offset and gain parameter for fine calibration for
 | |
|  *  TPA/ADF scanning
 | |
|  */
 | |
| static void usb_GetSWOffsetGain4TPA( Plustek_Device *dev )
 | |
| {
 | |
| 	ScanParam *param = &dev->scanning.sParam;
 | |
| 	DCapsDef  *sCaps = &dev->usbDev.Caps;
 | |
| 
 | |
| 	switch( sCaps->bCCD ) {
 | |
| 		case kEPSON:
 | |
| 			DBG( _DBG_INFO2, "kEPSON TPA adjustments\n" );
 | |
| 			param->swGain[0] = 1000;
 | |
| 			param->swGain[1] = 1000;
 | |
| 			param->swGain[2] = 1000;
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** usb_GetSWOffsetGain
 | |
|  * preset the offset and gain parameter for fine calibration for normal
 | |
|  * scanning
 | |
|  */
 | |
| static void usb_GetSWOffsetGain( Plustek_Device *dev )
 | |
| {
 | |
| 	ScanParam *param = &dev->scanning.sParam;
 | |
| 	DCapsDef  *sCaps  = &dev->usbDev.Caps;
 | |
| 	HWDef     *hw     = &dev->usbDev.HwSetting;
 | |
| 
 | |
| 	param->swOffset[0] = 0;
 | |
| 	param->swOffset[1] = 0;
 | |
| 	param->swOffset[2] = 0;
 | |
| 
 | |
| 	param->swGain[0] = 1000;
 | |
| 	param->swGain[1] = 1000;
 | |
| 	param->swGain[2] = 1000;
 | |
| 
 | |
| 	if( param->bSource != SOURCE_Reflection ) {
 | |
| 		usb_GetSWOffsetGain4TPA( dev );
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* only valid for normal scanning... */
 | |
| 	switch( sCaps->bCCD ) {
 | |
| 
 | |
| 	case kEPSON:
 | |
| 		DBG( _DBG_INFO2, "kEPSON adjustments\n" );
 | |
| #if 0
 | |
| 		param->swGain[0] = 800;
 | |
| 		param->swGain[1] = 800;
 | |
| 		param->swGain[2] = 800;
 | |
| #endif
 | |
| 		break;
 | |
| 	
 | |
| 	case kNECSLIM:
 | |
| 		DBG( _DBG_INFO2, "kNECSLIM adjustments\n" );
 | |
| 		if( param->PhyDpi.x <= 150 ) {
 | |
| 			param->swOffset[0] = 600;
 | |
| 			param->swOffset[1] = 500;
 | |
| 			param->swOffset[2] = 300;
 | |
| 			param->swGain[0] = 960;
 | |
| 			param->swGain[1] = 970;
 | |
| 			param->swGain[2] = 1000;
 | |
| 		} else if (param->PhyDpi.x <= 300) {
 | |
| 			param->swOffset[0] = 700;
 | |
| 			param->swOffset[1] = 600;
 | |
| 			param->swOffset[2] = 400;
 | |
| 			param->swGain[0] = 967;
 | |
| 			param->swGain[1] = 980;
 | |
| 			param->swGain[2] = 1000;
 | |
| 		} else {
 | |
| 			param->swOffset[0] = 900;
 | |
| 			param->swOffset[1] = 850;
 | |
| 			param->swOffset[2] = 620;
 | |
| 			param->swGain[0] = 965;
 | |
| 			param->swGain[1] = 980;
 | |
| 			param->swGain[2] = 1000;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case kNEC8861:
 | |
| 		DBG( _DBG_INFO2, "kNEC8861 adjustments\n" );
 | |
| 		break;
 | |
| 
 | |
| 	case kCIS670:
 | |
| 		DBG( _DBG_INFO2, "kCIS670 adjustments\n" );
 | |
| 		if(param->bDataType == SCANDATATYPE_Color) {
 | |
| 
 | |
| 			param->swGain[0] =
 | |
| 			param->swGain[1] =
 | |
| 			param->swGain[2] = 952;
 | |
| 
 | |
| 			param->swOffset[0] =
 | |
| 			param->swOffset[1] =
 | |
| 			param->swOffset[2] = 1000;
 | |
| 		}
 | |
| 		break;
 | |
| #if 0
 | |
| 	case kCIS650:
 | |
| 	case kCIS1220:
 | |
| 
 | |
| 		DBG( _DBG_INFO2, "kCIS adjustments\n" );
 | |
| 		if(param->bDataType == SCANDATATYPE_Color) {
 | |
| 
 | |
| 			param->swGain[0] =
 | |
| 			param->swGain[1] =
 | |
| 			param->swGain[2] = 952;
 | |
| 
 | |
| 			param->swOffset[0] =
 | |
| 			param->swOffset[1] =
 | |
| 			param->swOffset[2] = 1000;
 | |
| 		}
 | |
| 		break;
 | |
| 	case kCIS1240:
 | |
| 		DBG( _DBG_INFO2, "kCIS1240 adjustments\n" );
 | |
| 		if(param->bDataType == SCANDATATYPE_Color) {
 | |
| 
 | |
| 			param->swGain[0] = 950;
 | |
| 			param->swGain[1] = 950;
 | |
| 			param->swGain[2] = 900;
 | |
| 
 | |
| 			param->swOffset[0] =
 | |
| 			param->swOffset[1] =
 | |
| 			param->swOffset[2] = 0; /*1000;*/
 | |
| 		}
 | |
| 		break;
 | |
| #endif
 | |
| 
 | |
| 	case kNEC3799:
 | |
| 		DBG( _DBG_INFO2, "kNEC3799 adjustments\n" );
 | |
| 		if( sCaps->bPCB == 2 ) {
 | |
| 			if( param->PhyDpi.x <= 150 ) {
 | |
| 				param->swOffset[0] = 600;
 | |
| 				param->swOffset[1] = 500;
 | |
| 				param->swOffset[2] = 300;
 | |
| 				param->swGain[0] = 960;
 | |
| 				param->swGain[1] = 970;
 | |
| 				param->swGain[2] = 1000;
 | |
| 			} else if (param->PhyDpi.x <= 300) {
 | |
| 				param->swOffset[0] = 700;
 | |
| 				param->swOffset[1] = 600;
 | |
| 				param->swOffset[2] = 400;
 | |
| 				param->swGain[0] = 967;
 | |
| 				param->swGain[1] = 980;
 | |
| 				param->swGain[2] = 1000;
 | |
| 			} else {
 | |
| 				param->swOffset[0] = 900;
 | |
| 				param->swOffset[1] = 850;
 | |
| 				param->swOffset[2] = 620;
 | |
| 				param->swGain[0] = 965;
 | |
| 				param->swGain[1] = 980;
 | |
| 				param->swGain[2] = 1000;
 | |
| 			}
 | |
| 		} else if( hw->motorModel == MODEL_KaoHsiung ) {
 | |
| 			param->swOffset[0] = 1950;
 | |
| 			param->swOffset[1] = 1700;
 | |
| 			param->swOffset[2] = 1250;
 | |
| 			param->swGain[0] = 955;
 | |
| 			param->swGain[1] = 950;
 | |
| 			param->swGain[2] = 1000;
 | |
| 		} else { /* MODEL_Hualien */
 | |
| 			if( param->PhyDpi.x <= 300 ) {
 | |
| 				if( param->bBitDepth > 8 ) {
 | |
| 					param->swOffset[0] = 0;
 | |
| 					param->swOffset[1] = 0;
 | |
| 					param->swOffset[2] = -300;
 | |
| 					param->swGain[0] = 970;
 | |
| 					param->swGain[1] = 985;
 | |
| 					param->swGain[2] = 1050;
 | |
| 				} else {
 | |
| 					param->swOffset[0] = -485;
 | |
| 					param->swOffset[1] = -375;
 | |
| 					param->swOffset[2] = -628;
 | |
| 					param->swGain[0] = 970;
 | |
| 					param->swGain[1] = 980;
 | |
| 					param->swGain[2] = 1050;
 | |
| 				}
 | |
| 			} else {
 | |
| 				if( param->bBitDepth > 8 ) {
 | |
| 					param->swOffset[0] = 1150;
 | |
| 					param->swOffset[1] = 1000;
 | |
| 					param->swOffset[2] = 700;
 | |
| 					param->swGain[0] = 990;
 | |
| 					param->swGain[1] = 1000;
 | |
| 					param->swGain[2] = 1050;
 | |
| 				} else {
 | |
| 					param->swOffset[0] = -30;
 | |
| 					param->swOffset[1] = 0;
 | |
| 					param->swOffset[2] = -250;
 | |
| 					param->swGain[0] = 985;
 | |
| 					param->swGain[1] = 995;
 | |
| 					param->swGain[2] = 1050;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case kSONY548:
 | |
| 		DBG( _DBG_INFO2, "kSONY548 adjustments\n" );
 | |
| 		if(param->bDataType == SCANDATATYPE_Color)
 | |
| 		{
 | |
| 			if(param->PhyDpi.x <= 75)
 | |
| 			{
 | |
| 				param->swOffset[0] = 650;
 | |
| 				param->swOffset[1] = 850;
 | |
| 				param->swOffset[2] = 500;
 | |
| 				param->swGain[0] = 980;
 | |
| 				param->swGain[1] = 1004;
 | |
| 				param->swGain[2] = 1036;
 | |
| 			}
 | |
| 			else if(param->PhyDpi.x <= 300)
 | |
| 			{
 | |
| 				param->swOffset[0] = 700;
 | |
| 				param->swOffset[1] = 900;
 | |
| 				param->swOffset[2] = 550;
 | |
| 				param->swGain[0] = 970;
 | |
| 				param->swGain[1] = 995;
 | |
| 				param->swGain[2] = 1020;
 | |
| 			}
 | |
| 			else if(param->PhyDpi.x <= 400)
 | |
| 			{
 | |
| 				param->swOffset[0] = 770;
 | |
| 				param->swOffset[1] = 1010;
 | |
| 				param->swOffset[2] = 600;
 | |
| 				param->swGain[0] = 970;
 | |
| 				param->swGain[1] = 993;
 | |
| 				param->swGain[2] = 1023;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				param->swOffset[0] = 380;
 | |
| 				param->swOffset[1] = 920;
 | |
| 				param->swOffset[2] = 450;
 | |
| 				param->swGain[0] = 957;
 | |
| 				param->swGain[1] = 980;
 | |
| 				param->swGain[2] = 1008;
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			if(param->PhyDpi.x <= 75)
 | |
| 			{
 | |
| 				param->swOffset[1] = 1250;
 | |
| 				param->swGain[1] = 950;
 | |
| 			}
 | |
| 			else if(param->PhyDpi.x <= 300)
 | |
| 			{
 | |
| 				param->swOffset[1] = 1250;
 | |
| 				param->swGain[1] = 950;
 | |
| 			}
 | |
| 			else if(param->PhyDpi.x <= 400)
 | |
| 			{
 | |
| 				param->swOffset[1] = 1250;
 | |
| 				param->swGain[1] = 950;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				param->swOffset[1] = 1250;
 | |
| 				param->swGain[1] = 950;
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case kNEC3778:
 | |
| 		DBG( _DBG_INFO2, "kNEC3778 adjustments\n" );
 | |
| 		if((_LM9831 == hw->chip) && (param->PhyDpi.x <= 300))
 | |
| 		{
 | |
| 			param->swOffset[0] = 0;
 | |
| 			param->swOffset[1] = 0;
 | |
| 			param->swOffset[2] = 0;
 | |
| 			param->swGain[0] = 900;
 | |
| 			param->swGain[1] = 920;
 | |
| 			param->swGain[2] = 980;
 | |
| 		}
 | |
| 		else if( hw->motorModel == MODEL_HuaLien && param->PhyDpi.x > 800)
 | |
| 		{
 | |
| 			param->swOffset[0] = 0;
 | |
| 			param->swOffset[1] = 0;
 | |
| 			param->swOffset[2] = -200;
 | |
| 			param->swGain[0] = 980;
 | |
| 			param->swGain[1] = 930;
 | |
| 			param->swGain[2] = 1080;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			param->swOffset[0] = -304;
 | |
| 			param->swOffset[1] = -304;
 | |
| 			param->swOffset[2] = -304;
 | |
| 			param->swGain[0] = 910;	
 | |
| 			param->swGain[1] = 920;	
 | |
| 			param->swGain[2] = 975;
 | |
| 		}
 | |
| 		
 | |
| 		if(param->bDataType == SCANDATATYPE_BW && param->PhyDpi.x <= 300)
 | |
| 		{
 | |
| 			param->swOffset[1] = 1000;
 | |
| 			param->swGain[1] = 1000;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** according to the pixel values,
 | |
|  */
 | |
| static u_char usb_GetNewGain( Plustek_Device *dev, u_short wMax, int channel )
 | |
| {
 | |
| 	double dRatio, dAmp;
 | |
| 	u_long dwInc, dwDec;
 | |
| 	u_char bGain;
 | |
| 
 | |
| 	if( !wMax )
 | |
| 		wMax = 1;
 | |
| 
 | |
| 	dAmp = 0.93 + 0.067 * dev->usbDev.a_bRegs[0x3b+channel];
 | |
| 
 | |
| 	if((m_dwIdealGain / (wMax / dAmp)) < 3) {
 | |
| 
 | |
| 		dRatio = ((double) m_dwIdealGain * dAmp / wMax - 0.93) / 0.067;
 | |
| 		if(ceil(dRatio) > 0x3f)
 | |
| 			return 0x3f;
 | |
| 
 | |
| 		dwInc = (u_long)((0.93 + ceil (dRatio) * 0.067) * wMax / dAmp);
 | |
| 		dwDec = (u_long)((0.93 + floor (dRatio) * 0.067) * wMax / dAmp);
 | |
| 		if((dwInc >= 0xff00) ||
 | |
| 		   (labs (dwInc - m_dwIdealGain) > labs(dwDec - m_dwIdealGain))) {
 | |
| 			bGain = (u_char)floor(dRatio);
 | |
| 		} else {
 | |
| 			bGain = (u_char)ceil(dRatio);
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 
 | |
| 		dRatio = m_dwIdealGain / (wMax / dAmp);
 | |
| 		dAmp   = floor((dRatio / 3 - 0.93)/0.067);
 | |
| 
 | |
| 		if( dAmp > 31 )
 | |
| 			dAmp = 31;
 | |
| 
 | |
| 		bGain = (u_char)dAmp + 32;
 | |
| 	}
 | |
| 	
 | |
| 	if( bGain > 0x3f ) {
 | |
| 		DBG( _DBG_INFO, "* GAIN Overflow!!!\n" );
 | |
| 		bGain = 0x3f;
 | |
| 	}
 | |
| 	return bGain;
 | |
| }
 | |
| 
 | |
| /** limit and set register given by address
 | |
|  * @param gain -
 | |
|  * @param reg  -
 | |
|  */
 | |
| static void setAdjGain( int gain, u_char *reg )
 | |
| {
 | |
| 	if( gain >= 0 ) {
 | |
| 
 | |
| 		if( gain > 0x3f )
 | |
| 			*reg = 0x3f;
 | |
| 		else
 | |
| 			*reg = gain;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param channel - 0 = red, 1 = green, 2 = blue
 | |
|  * @param max     -
 | |
|  * @param ideal   -
 | |
|  * @param l_on    -
 | |
|  * @param l_off   -
 | |
|  * @return
 | |
|  */
 | |
| static SANE_Bool adjLampSetting( Plustek_Device *dev, int channel, u_long max,  
 | |
|                                  u_long ideal, u_short l_on, u_short *l_off )
 | |
| {
 | |
| 	SANE_Bool adj = SANE_FALSE;
 | |
| 	u_long    lamp_on;
 | |
| 
 | |
| 	/* so if the image was too bright, we dim the lamps by 3% */
 | |
| 	if( max > ideal ) {
 | |
| 
 | |
| 		lamp_on = (*l_off) - l_on;
 | |
| 	    lamp_on = (lamp_on * 97)/100;
 | |
| 		*l_off  = l_on + lamp_on;
 | |
|         DBG( _DBG_INFO2,
 | |
| 	                "lamp(%u) adjust (-3%%): %i %i\n", channel, l_on, *l_off );
 | |
| 		adj = SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
|     /* if the image was too dull, increase lamp by 1% */
 | |
| 	if( dev->usbDev.a_bRegs[0x3b + channel] == 63 ) {
 | |
| 
 | |
| 		lamp_on  = (*l_off) - l_on;
 | |
| 	    lamp_on += (lamp_on/100);
 | |
| 		*l_off   = l_on + lamp_on;
 | |
|         DBG( _DBG_INFO2,
 | |
| 	                "lamp(%u) adjust (+1%%): %i %i\n", channel, l_on, *l_off );
 | |
| 		adj = SANE_TRUE;
 | |
|     }
 | |
| 
 | |
|     return adj;
 | |
| }
 | |
| 
 | |
| /** usb_AdjustGain
 | |
|  * function to perform the "coarse calibration step" part 1.
 | |
|  * We scan reference image pixels to determine the optimum coarse gain settings
 | |
|  * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
 | |
|  * applied at the line rate during normal scanning.
 | |
|  * The scanned line should contain a white strip with some black at the
 | |
|  * beginning. The function searches for the maximum value which corresponds to
 | |
|  * the maximum white value.
 | |
|  * Affects register 0x3b, 0x3c and 0x3d
 | |
|  *
 | |
|  */
 | |
| static SANE_Bool usb_AdjustGain( Plustek_Device *dev, int fNegative )
 | |
| {
 | |
| 	char      tmp[40];
 | |
| 	int       i;
 | |
| 	double    min_mclk;
 | |
| 	ScanDef  *scanning = &dev->scanning;
 | |
| 	DCapsDef *scaps    = &dev->usbDev.Caps;
 | |
| 	HWDef    *hw       = &dev->usbDev.HwSetting;
 | |
| 	u_long   *scanbuf  = scanning->pScanBuffer;
 | |
| 	u_char   *regs     = dev->usbDev.a_bRegs;
 | |
| 	u_long    dw, start, end, len;
 | |
| 	SANE_Bool fRepeatITA = SANE_TRUE;
 | |
| 
 | |
| 	if( usb_IsEscPressed())
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	bMaxITA = 0xff;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "#########################\n" );
 | |
| 	DBG( _DBG_INFO, "usb_AdjustGain()\n" );
 | |
| 	if((dev->adj.rgain != -1) && 
 | |
| 	   (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
 | |
| 		setAdjGain( dev->adj.rgain, ®s[0x3b] );
 | |
| 		setAdjGain( dev->adj.ggain, ®s[0x3c] );
 | |
| 		setAdjGain( dev->adj.bgain, ®s[0x3d] );
 | |
| 		DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
 | |
| 		return SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
| 	min_mclk = usb_GetMCLK( dev, &scanning->sParam );
 | |
| 
 | |
| 	/* define the strip to scan for coarse calibration */
 | |
| 	m_ScanParam.Size.dwLines  = 1;
 | |
| 	m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
 | |
| 	                            scaps->OpticDpi.x / 300UL;
 | |
| 	m_ScanParam.Size.dwBytes  = m_ScanParam.Size.dwPixels *
 | |
| 	                            2 * m_ScanParam.bChannels;
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color)
 | |
| 		m_ScanParam.Size.dwBytes *= 3;
 | |
| 
 | |
| 	m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
 | |
|                                                     300UL / scaps->OpticDpi.x);
 | |
| 	m_ScanParam.bCalibration = PARAM_Gain;
 | |
| 
 | |
| 	start = 0;
 | |
| 	len   = m_ScanParam.Size.dwPixels;
 | |
| 
 | |
| 	if( scanning->sParam.bSource == SOURCE_Transparency ) {
 | |
| 		start = scaps->Positive.DataOrigin.x * scaps->OpticDpi.x / 300UL;
 | |
| 		len   = scaps->Positive.Size.x * scaps->OpticDpi.x / 300UL;
 | |
| 	}
 | |
| 	else if( scanning->sParam.bSource == SOURCE_Negative ) {
 | |
| 		start = scaps->Negative.DataOrigin.x * scaps->OpticDpi.x / 300UL;
 | |
| 		len   = scaps->Negative.Size.x * scaps->OpticDpi.x / 300UL;
 | |
| 	}
 | |
| 	end = start + len;
 | |
| 
 | |
| 	start = ((u_long)dev->usbDev.pSource->DataOrigin.x*scaps->OpticDpi.x/300UL);
 | |
| 	len   = ((u_long)dev->usbDev.pSource->Size.x * scaps->OpticDpi.x / 300UL);
 | |
| 
 | |
| 	DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" );
 | |
| 	DBG( _DBG_INFO2, "Lines    = %lu\n", m_ScanParam.Size.dwLines  );
 | |
| 	DBG( _DBG_INFO2, "Pixels   = %lu\n", m_ScanParam.Size.dwPixels );
 | |
| 	DBG( _DBG_INFO2, "Bytes    = %lu\n", m_ScanParam.Size.dwBytes  );
 | |
| 	DBG( _DBG_INFO2, "Origin.X = %u\n",  m_ScanParam.Origin.x );
 | |
| 	DBG( _DBG_INFO2, "Start    = %lu\n", start );
 | |
| 	DBG( _DBG_INFO2, "Len      = %lu\n", len   );
 | |
| 	DBG( _DBG_INFO2, "End      = %lu\n", end   );
 | |
| 	DBG( _DBG_INFO,  "MIN MCLK = %.2f\n", min_mclk );
 | |
| 
 | |
| 	i = 0;
 | |
| TOGAIN:
 | |
| 	m_ScanParam.dMCLK = dMCLK;
 | |
| 
 | |
| 	if( !usb_SetScanParameters( dev, &m_ScanParam ))
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	if( !usb_ScanBegin( dev, SANE_FALSE) ||
 | |
| 		!usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
 | |
| 		!usb_ScanEnd( dev )) {
 | |
| 		DBG( _DBG_ERROR, "usb_AdjustGain() failed\n" );
 | |
| 		return SANE_FALSE;
 | |
| 	}
 | |
| 
 | |
| 	DBG( _DBG_INFO2, "PhyBytes  = %lu\n",  m_ScanParam.Size.dwPhyBytes  );
 | |
| 	DBG( _DBG_INFO2, "PhyPixels = %lu\n",  m_ScanParam.Size.dwPhyPixels );
 | |
| 
 | |
| 	if( end > m_ScanParam.Size.dwPhyPixels )
 | |
| 		end = m_ScanParam.Size.dwPhyPixels;
 | |
| 
 | |
| 	sprintf( tmp, "coarse-gain-%u.raw", i++ );
 | |
| 
 | |
| 	dumpPicInit(&m_ScanParam, tmp);
 | |
| 	dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
 | |
| 		
 | |
| #ifdef SWAP_COARSE
 | |
| 	if(usb_HostSwap())
 | |
| #endif
 | |
| 		usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
 | |
| 
 | |
| 	if( fNegative ) {
 | |
| 
 | |
| 		if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 			RGBULongDef rgb, rgbSum;
 | |
| 			u_long      dwLoop = len / 20 * 20;
 | |
| 			u_long      dw10, dwGray, dwGrayMax;
 | |
| 
 | |
| 			rgb.Red = rgb.Green = rgb.Blue = dwGrayMax = 0;
 | |
| 
 | |
| 			for( dw = start; dwLoop; dwLoop-- ) {
 | |
| 
 | |
| 				rgbSum.Red = rgbSum.Green = rgbSum.Blue = 0;
 | |
| 				for( dw10 = 20; dw10--; dw++ ) {
 | |
| 					rgbSum.Red   += (u_long)(((RGBULongDef*)scanbuf)[dw].Red);
 | |
| 					rgbSum.Green += (u_long)(((RGBULongDef*)scanbuf)[dw].Green);
 | |
| 					rgbSum.Blue  += (u_long)(((RGBULongDef*)scanbuf)[dw].Blue);
 | |
| 				}
 | |
| 
 | |
| 				/* do some weighting of the color planes for negatives */
 | |
| 				dwGray = (rgbSum.Red * 30UL + rgbSum.Green * 59UL + rgbSum.Blue * 11UL) / 100UL;
 | |
| 
 | |
| 				if( fNegative == 1 || rgbSum.Red > rgbSum.Green) {
 | |
| 					if( dwGray > dwGrayMax ) {
 | |
| 						dwGrayMax = dwGray;
 | |
| 						rgb.Red   = rgbSum.Red;
 | |
| 						rgb.Green = rgbSum.Green;
 | |
| 						rgb.Blue  = rgbSum.Blue;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			Gain_Hilight.Red   = (u_short)(rgb.Red / 20UL);
 | |
| 			Gain_Hilight.Green = (u_short)(rgb.Green / 20UL);
 | |
| 			Gain_Hilight.Blue  = (u_short)(rgb.Blue / 20UL);
 | |
| 			DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
 | |
| 			    Gain_Hilight.Red, Gain_Hilight.Red, Gain_Hilight.Green, 
 | |
| 			    Gain_Hilight.Green, Gain_Hilight.Blue, Gain_Hilight.Blue );
 | |
| 
 | |
| 			regs[0x3b] = usb_GetNewGain(dev,Gain_Hilight.Red,   0 );
 | |
| 			regs[0x3c] = usb_GetNewGain(dev,Gain_Hilight.Green, 1 );
 | |
| 			regs[0x3d] = usb_GetNewGain(dev,Gain_Hilight.Blue,  2 );
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			u_long dwMax  = 0, dwSum;
 | |
| 			u_long dwLoop = len / 20 * 20;
 | |
| 			u_long dw10;
 | |
| 
 | |
| 			for( dw = start; dwLoop; dwLoop-- ) {
 | |
| 
 | |
| 				dwSum = 0;
 | |
| 				for( dw10 = 20; dw10--; dw++ )
 | |
| 					dwSum += (u_long)((u_short*)scanbuf)[dw];
 | |
| 
 | |
| 				if((fNegative == 1) || (dwSum < 0x6000 * 20)) {
 | |
| 					if( dwMax < dwSum )
 | |
| 						dwMax = dwSum;
 | |
| 				}
 | |
| 			}
 | |
| 			Gain_Hilight.Red  = Gain_Hilight.Green =
 | |
| 			Gain_Hilight.Blue = (u_short)(dwMax / 20UL);
 | |
| 
 | |
| 			Gain_Reg.Red  = Gain_Reg.Green =
 | |
| 			Gain_Reg.Blue = regs[0x3b] =
 | |
| 			regs[0x3c] = regs[0x3d] = usb_GetNewGain(dev,Gain_Hilight.Green,1);
 | |
| 		}
 | |
| 	} else {
 | |
| 	
 | |
| 		if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 			RGBUShortDef max_rgb, min_rgb, tmp_rgb;
 | |
| 			u_long       dwR, dwG, dwB;
 | |
| 			u_long       dwDiv   = 10;
 | |
| 			u_long       dwLoop1 = len / dwDiv, dwLoop2;
 | |
| 
 | |
| 			max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0;
 | |
| 			min_rgb.Red = min_rgb.Green = min_rgb.Blue = 0xffff;
 | |
| 
 | |
| 			/* find out the max pixel value for R, G, B */
 | |
| 			for( dw = start; dwLoop1; dwLoop1-- ) {
 | |
| 
 | |
| 				/* do some averaging... */
 | |
| 				for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0;
 | |
| 				                                    dwLoop2; dwLoop2--, dw++) {
 | |
| 					if( usb_IsCISDevice(dev)) {
 | |
| 						dwR += ((u_short*)scanbuf)[dw];
 | |
| 						dwG += ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
 | |
| 						dwB += ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
 | |
|             		} else {
 | |
| 						dwR += ((RGBUShortDef*)scanbuf)[dw].Red;
 | |
| 						dwG += ((RGBUShortDef*)scanbuf)[dw].Green;
 | |
| 						dwB += ((RGBUShortDef*)scanbuf)[dw].Blue;
 | |
| 					}
 | |
| 				}
 | |
| 				dwR = dwR / dwDiv;
 | |
| 				dwG = dwG / dwDiv;
 | |
| 				dwB = dwB / dwDiv;
 | |
| 
 | |
| 				if(max_rgb.Red < dwR)
 | |
| 					max_rgb.Red = dwR;
 | |
| 				if(max_rgb.Green < dwG)
 | |
| 					max_rgb.Green = dwG;
 | |
| 				if(max_rgb.Blue < dwB)
 | |
| 					max_rgb.Blue = dwB;
 | |
| 
 | |
| 				if(min_rgb.Red > dwR)
 | |
| 					min_rgb.Red = dwR;
 | |
| 				if(min_rgb.Green > dwG)
 | |
| 					min_rgb.Green = dwG;
 | |
| 				if(min_rgb.Blue > dwB)
 | |
| 					min_rgb.Blue = dwB;
 | |
| 			}
 | |
| 
 | |
| 			DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
 | |
| 				  	max_rgb.Red, max_rgb.Red, max_rgb.Green,
 | |
| 					max_rgb.Green, max_rgb.Blue, max_rgb.Blue );
 | |
| 			DBG(_DBG_INFO2, "MIN(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
 | |
| 				  	min_rgb.Red, min_rgb.Red, min_rgb.Green,
 | |
| 					min_rgb.Green, min_rgb.Blue, min_rgb.Blue );
 | |
| 
 | |
| 			/* on CIS scanner, we use the min value, on CCD the max value
 | |
| 			 * for adjusting the gain
 | |
| 			 */
 | |
| 			tmp_rgb = max_rgb;
 | |
| 			if( usb_IsCISDevice(dev))
 | |
| 				tmp_rgb = min_rgb;
 | |
| 
 | |
| 			DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n",
 | |
| 				  	tmp_rgb.Red, tmp_rgb.Red, tmp_rgb.Green,
 | |
| 				    tmp_rgb.Green, tmp_rgb.Blue, tmp_rgb.Blue);
 | |
| 
 | |
| /*			m_dwIdealGain = IDEAL_GainNormal;
 | |
| */						 /* min(min(rgb.wRed, rgb.wGreen), rgb.wBlue) */
 | |
| 
 | |
| 			regs[0x3b] = usb_GetNewGain( dev, tmp_rgb.Red,   0 );
 | |
| 			regs[0x3c] = usb_GetNewGain( dev, tmp_rgb.Green, 1 );
 | |
| 			regs[0x3d] = usb_GetNewGain( dev, tmp_rgb.Blue,  2 );
 | |
| 
 | |
| 			if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
 | |
| 
 | |
| 				SANE_Bool adj = SANE_FALSE;
 | |
| 
 | |
| 				/* on CIS devices, we can control the lamp off settings */
 | |
| 				if( usb_IsCISDevice(dev)) {
 | |
| 
 | |
| /*					m_dwIdealGain = IDEAL_GainNormal;
 | |
|  */
 | |
| 					if( adjLampSetting( dev, CHANNEL_red, tmp_rgb.Red, m_dwIdealGain,
 | |
| 					                    hw->red_lamp_on, &hw->red_lamp_off )) {
 | |
| 						adj = SANE_TRUE;
 | |
| 					}
 | |
| 
 | |
| 					if( adjLampSetting( dev, CHANNEL_green, tmp_rgb.Green, m_dwIdealGain,
 | |
| 								    hw->green_lamp_on, &hw->green_lamp_off )) {
 | |
| 						adj = SANE_TRUE;
 | |
| 					}
 | |
| 
 | |
| 					if( adjLampSetting( dev, CHANNEL_blue, tmp_rgb.Blue, m_dwIdealGain,
 | |
| 									    hw->blue_lamp_on, &hw->blue_lamp_off)){
 | |
| 						adj = SANE_TRUE;
 | |
| 					}
 | |
| 
 | |
| 					/* on any adjustment, set the registers... */
 | |
| 					if( adj ) {
 | |
| 						usb_AdjustLamps( dev, SANE_TRUE );
 | |
| 
 | |
| 						if( i < _MAX_GAIN_LOOPS )
 | |
| 							goto TOGAIN;
 | |
| 					}
 | |
| 
 | |
| 				} else {
 | |
| 
 | |
| 					if((!regs[0x3b] ||
 | |
| 					    !regs[0x3c] || !regs[0x3d]) && dMCLK > min_mclk) {
 | |
| 
 | |
| 						scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
 | |
| 						regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 						
 | |
| 						adj = SANE_TRUE;
 | |
| 
 | |
| 					} else if(((regs[0x3b] == 63) || (regs[0x3c] == 63) ||
 | |
| 									  (regs[0x3d] == 63)) && (dMCLK < 10)) {
 | |
| 
 | |
| 						scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
 | |
| 						regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 						adj = SANE_TRUE;
 | |
| 					}
 | |
| 
 | |
| 					if( adj ) {
 | |
| 						if( i < _MAX_GAIN_LOOPS )
 | |
| 							goto TOGAIN;
 | |
| 					}
 | |
| 				}
 | |
| 				
 | |
| 			} else {
 | |
| 
 | |
| 				/* for MODEL KaoHsiung 1200 scanner multi-straight-line bug at
 | |
| 				 * 1200 dpi color mode
 | |
| 				 */
 | |
| 				if( hw->motorModel == MODEL_KaoHsiung &&
 | |
| 				    scaps->bCCD == kNEC3778 && dMCLK>= 5.5 && !regs[0x3c]){
 | |
| 
 | |
| 					regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 					scanning->sParam.dMCLK = dMCLK = dMCLK - 1.5;
 | |
| 					goto TOGAIN;
 | |
| 
 | |
| 				} else if( hw->motorModel == MODEL_HuaLien &&
 | |
| 					       scaps->bCCD == kNEC3799 && fRepeatITA ) {
 | |
| 
 | |
| 					if((!regs[0x3b] ||
 | |
| 					    !regs[0x3c] || !regs[0x3d]) && dMCLK > 3.0) {
 | |
| 
 | |
| 						scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
 | |
| 						regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 						goto TOGAIN;
 | |
| 
 | |
| 					} else if(((regs[0x3b] == 63) || (regs[0x3c] == 63) ||
 | |
| 										  (regs[0x3d] == 63)) && (dMCLK < 10)) {
 | |
| 
 | |
| 						scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
 | |
| 						regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 						goto TOGAIN;
 | |
| 					}
 | |
| 					bMaxITA = (u_char)floor((dMCLK + 1) / 2);
 | |
| 					fRepeatITA = SANE_FALSE;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			u_short	w_max = 0, w_min = 0xffff, w_tmp;
 | |
| 
 | |
| 			for( dw = start; dw < end; dw++ ) {
 | |
| 				if( w_max < ((u_short*)scanbuf)[dw])
 | |
| 					w_max = ((u_short*)scanbuf)[dw];
 | |
| 				if( w_min > ((u_short*)scanbuf)[dw])
 | |
| 					w_min = ((u_short*)scanbuf)[dw];
 | |
| 			}
 | |
| 
 | |
| 			w_tmp = w_max;
 | |
| 			if( usb_IsCISDevice(dev))
 | |
| 				w_tmp = w_min;
 | |
| 
 | |
| 			regs[0x3b] =
 | |
| 			regs[0x3c] =
 | |
| 			regs[0x3d] = usb_GetNewGain(dev, w_tmp, 0);
 | |
| 
 | |
| 			DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max );
 | |
| 			DBG(_DBG_INFO2, "MIN(G)= 0x%04x(%u)\n", w_min, w_min );
 | |
| 			DBG(_DBG_INFO2, "CUR(G)= 0x%04x(%u)\n", w_tmp, w_tmp );
 | |
| 
 | |
| /*			m_dwIdealGain = IDEAL_GainNormal;
 | |
|  */
 | |
| 			if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
 | |
| 
 | |
| 				SANE_Bool adj = SANE_FALSE;
 | |
| 
 | |
| 				/* on CIS devices, we can control the lamp off settings */
 | |
| 				if( usb_IsCISDevice(dev)) {
 | |
| 
 | |
| 					if( adjLampSetting( dev, CHANNEL_green, w_tmp, m_dwIdealGain,
 | |
| 					                    hw->green_lamp_on, &hw->green_lamp_off )) {
 | |
| 						adj = SANE_TRUE;
 | |
| 					}
 | |
| 
 | |
| 					/* on any adjustment, set the registers... */
 | |
| 					if( adj ) {
 | |
| 						usb_AdjustLamps( dev, SANE_TRUE );
 | |
| 
 | |
| 						if( i < _MAX_GAIN_LOOPS )
 | |
| 							goto TOGAIN;
 | |
| 					}
 | |
| 
 | |
| 				} else {
 | |
| 
 | |
| 					if( !regs[0x3b] && (dMCLK > min_mclk)) {
 | |
| 
 | |
| 						scanning->sParam.dMCLK = dMCLK = dMCLK - 0.5;
 | |
| 						regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 						adj = SANE_TRUE;
 | |
| 
 | |
| 					} else if((regs[0x3b] == 63) && (dMCLK < 20)) {
 | |
| 
 | |
| 						scanning->sParam.dMCLK = dMCLK = dMCLK + 0.5;
 | |
| 						regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 						adj = SANE_TRUE;
 | |
| 					}
 | |
| 
 | |
| 					if( adj ) {
 | |
| 						if( i < _MAX_GAIN_LOOPS )
 | |
| 							goto TOGAIN;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DBG( _DBG_INFO2, "REG[0x3b] = %u\n", regs[0x3b] );
 | |
| 	DBG( _DBG_INFO2, "REG[0x3c] = %u\n", regs[0x3c] );
 | |
| 	DBG( _DBG_INFO2, "REG[0x3d] = %u\n", regs[0x3d] );
 | |
| 
 | |
| 	DBG( _DBG_INFO2, "red_lamp_on    = %u\n", hw->red_lamp_on  );
 | |
| 	DBG( _DBG_INFO2, "red_lamp_off   = %u\n", hw->red_lamp_off );
 | |
| 	DBG( _DBG_INFO2, "green_lamp_on  = %u\n", hw->green_lamp_on  );
 | |
| 	DBG( _DBG_INFO2, "green_lamp_off = %u\n", hw->green_lamp_off );
 | |
| 	DBG( _DBG_INFO2, "blue_lamp_on   = %u\n", hw->blue_lamp_on   );
 | |
| 	DBG( _DBG_INFO2, "blue_lamp_off  = %u\n", hw->blue_lamp_off  );
 | |
| 
 | |
| 	DBG( _DBG_INFO, "usb_AdjustGain() done.\n" );
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /** usb_GetNewOffset
 | |
|  * @param pdwSum   -
 | |
|  * @param pdwDiff  -
 | |
|  * @param pcOffset -
 | |
|  * @param pIdeal   -
 | |
|  * @param channel  -
 | |
|  * @param cAdjust  -
 | |
|  */
 | |
| static void usb_GetNewOffset( Plustek_Device *dev, u_long *pdwSum, u_long *pdwDiff,
 | |
|                               signed char *pcOffset, u_char *pIdeal,
 | |
|                               u_long channel, signed char cAdjust )
 | |
| {
 | |
| 	/* IDEAL_Offset is currently set to 0x1000 = 4096 */
 | |
| 	u_long dwIdealOffset = IDEAL_Offset;
 | |
| 
 | |
| 	if( pdwSum[channel] > dwIdealOffset ) {
 | |
| 
 | |
| 		/* Over ideal value */
 | |
| 		pdwSum[channel] -= dwIdealOffset;
 | |
| 		if( pdwSum[channel] < pdwDiff[channel] ) {
 | |
| 			/* New offset is better than old one */
 | |
| 			pdwDiff[channel] = pdwSum[channel];
 | |
| 			pIdeal[channel]  = dev->usbDev.a_bRegs[0x38 + channel];
 | |
| 		}
 | |
| 		pcOffset[channel] -= cAdjust;
 | |
| 
 | |
| 	} else 	{
 | |
| 
 | |
| 		/* Below than ideal value */
 | |
| 		pdwSum[channel] = dwIdealOffset - pdwSum [channel];
 | |
| 		if( pdwSum[channel] < pdwDiff[channel] ) {
 | |
| 			/* New offset is better than old one */
 | |
| 			pdwDiff[channel] = pdwSum[channel];
 | |
| 			pIdeal[channel]  = dev->usbDev.a_bRegs[0x38 + channel];
 | |
| 		}
 | |
| 		pcOffset[channel] += cAdjust;
 | |
| 	}
 | |
| 
 | |
| 	if( pcOffset[channel] >= 0 )
 | |
| 		dev->usbDev.a_bRegs[0x38 + channel] = pcOffset[channel];
 | |
| 	else
 | |
| 		dev->usbDev.a_bRegs[0x38 + channel] = (u_char)(32 - pcOffset[channel]);
 | |
| }
 | |
| 
 | |
| /** usb_AdjustOffset
 | |
|  * function to perform the "coarse calibration step" part 2.
 | |
|  * We scan reference image pixels to determine the optimum coarse offset settings
 | |
|  * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are
 | |
|  * applied at the line rate during normal scanning.
 | |
|  * On CIS based devices, we switch the light off, on CCD devices, we use the optical
 | |
|  * black pixels.
 | |
|  * Affects register 0x38, 0x39 and 0x3a
 | |
|  */
 | |
| static SANE_Bool usb_AdjustOffset( Plustek_Device *dev )
 | |
| {
 | |
| 	char          tmp[40];
 | |
| 	signed char   cAdjust = 16;
 | |
| 	signed char   cOffset[3];
 | |
| 	u_char        bExpect[3];
 | |
| 	int           i;
 | |
| 	u_long        dw, dwPixels;
 | |
|     u_long        dwDiff[3], dwSum[3];
 | |
| 
 | |
| 	HWDef  *hw      = &dev->usbDev.HwSetting;
 | |
| 	u_char *regs    = dev->usbDev.a_bRegs;
 | |
| 	u_long *scanbuf = dev->scanning.pScanBuffer;
 | |
| 
 | |
| 	if( usb_IsEscPressed())
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "#########################\n" );
 | |
| 	DBG( _DBG_INFO, "usb_AdjustOffset()\n" );
 | |
| 	if((dev->adj.rofs != -1) && 
 | |
| 	   (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
 | |
| 		regs[0x38] = (dev->adj.rofs & 0x3f);
 | |
| 		regs[0x39] = (dev->adj.gofs & 0x3f);
 | |
| 		regs[0x3a] = (dev->adj.bofs & 0x3f);
 | |
| 		DBG( _DBG_INFO, "- function skipped, using frontend values!\n" );
 | |
| 		return SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
| 	m_ScanParam.Size.dwLines  = 1;
 | |
| 	m_ScanParam.Size.dwPixels = 2550;
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev))
 | |
| 		dwPixels = m_ScanParam.Size.dwPixels;
 | |
| 	else
 | |
| 		dwPixels = (u_long)(hw->bOpticBlackEnd - hw->bOpticBlackStart );
 | |
| 
 | |
| 	m_ScanParam.Size.dwPixels = 2550;
 | |
| 	m_ScanParam.Size.dwBytes  = m_ScanParam.Size.dwPixels * 2 *
 | |
| 	                                                     m_ScanParam.bChannels;
 | |
| 	if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
 | |
| 		m_ScanParam.Size.dwBytes *= 3;
 | |
| 
 | |
| 	m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL /
 | |
| 												  dev->usbDev.Caps.OpticDpi.x);
 | |
| 	m_ScanParam.bCalibration = PARAM_Offset;
 | |
|  	m_ScanParam.dMCLK        = dMCLK;
 | |
| 
 | |
| 	dwDiff[0]  = dwDiff[1]  = dwDiff[2]  = 0xffff;
 | |
| 	cOffset[0] = cOffset[1] = cOffset[2] = 0;
 | |
| 	bExpect[0] = bExpect[1] = bExpect[2] = 0;
 | |
| 
 | |
| 	regs[0x38] = regs[0x39] = regs[0x3a] = 0;
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev)) {
 | |
| 	    /*
 | |
| 		 * if we have dark shading strip, there's no need to switch
 | |
| 	     * the lamp off
 | |
| 		 */
 | |
| 		if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
 | |
| 
 | |
| 			usb_ModuleToHome( dev, SANE_TRUE );
 | |
| 			usb_ModuleMove  ( dev, MOVE_Forward,
 | |
| 								(u_long)dev->usbDev.pSource->DarkShadOrgY );
 | |
| 
 | |
| 			regs[0x45] &= ~0x10;
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 		 	/* switch lamp off to read dark data... */
 | |
| 			regs[0x29] = 0;
 | |
| 			usb_switchLamp( dev, SANE_FALSE );
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if( 0 == dwPixels ) {
 | |
| 		DBG( _DBG_ERROR, "OpticBlackEnd = OpticBlackStart!!!\n" );
 | |
| 		return SANE_FALSE;
 | |
| 	}
 | |
| 
 | |
| 	if( !usb_SetScanParameters( dev, &m_ScanParam )) {
 | |
| 		DBG( _DBG_ERROR, "usb_AdjustOffset() failed\n" );
 | |
| 		return SANE_FALSE;
 | |
| 	}
 | |
| 		
 | |
| 	i = 0;
 | |
| 
 | |
| 	DBG( _DBG_INFO2, "S.dwPixels  = %lu\n", m_ScanParam.Size.dwPixels );		
 | |
| 	DBG( _DBG_INFO2, "dwPixels    = %lu\n", dwPixels );
 | |
| 	DBG( _DBG_INFO2, "dwPhyBytes  = %lu\n", m_ScanParam.Size.dwPhyBytes );
 | |
| 	DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
 | |
| 
 | |
| 	while( cAdjust ) {
 | |
| 
 | |
| 		/*
 | |
| 		 * read data (a white calibration strip - hopefully ;-)
 | |
| 		 */
 | |
| 		if((!usb_ScanBegin(dev, SANE_FALSE)) ||
 | |
| 		   (!usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes)) ||
 | |
| 			!usb_ScanEnd( dev )) {
 | |
| 			DBG( _DBG_ERROR, "usb_AdjustOffset() failed\n" );
 | |
| 			return SANE_FALSE;
 | |
| 		}
 | |
| 
 | |
| 		sprintf( tmp, "coarse-off-%u.raw", i++ );
 | |
| 		
 | |
| #ifdef SWAP_COARSE
 | |
| 		if(usb_HostSwap())
 | |
| 			usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
 | |
| #endif
 | |
| 		dumpPicInit(&m_ScanParam, tmp);
 | |
| 		dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
 | |
| 
 | |
| 		if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 			dwSum[0] = dwSum[1] = dwSum[2] = 0;
 | |
| 
 | |
| 			for (dw = 0; dw < dwPixels; dw++) {
 | |
| #ifndef SWAP_COARSE
 | |
| 				dwSum[0] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[0]);
 | |
| 				dwSum[1] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[1]);
 | |
| 				dwSum[2] += (u_long)_HILO2WORD(((ColorWordDef*)scanbuf)[dw].HiLo[2]);
 | |
| #else
 | |
| 				dwSum[0] += ((RGBUShortDef*)scanbuf)[dw].Red;
 | |
| 				dwSum[1] += ((RGBUShortDef*)scanbuf)[dw].Green;
 | |
| 				dwSum[2] += ((RGBUShortDef*)scanbuf)[dw].Blue;
 | |
| #endif
 | |
| 			}
 | |
| 
 | |
|             DBG( _DBG_INFO2, "RedSum   = %lu, ave = %lu\n",
 | |
| 												dwSum[0], dwSum[0] /dwPixels );
 | |
|             DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu\n",
 | |
| 												dwSum[1], dwSum[1] /dwPixels );
 | |
|             DBG( _DBG_INFO2, "BlueSum  = %lu, ave = %lu\n",
 | |
| 												dwSum[2], dwSum[2] /dwPixels );
 | |
| 			
 | |
| 			/* do averaging for each channel */
 | |
| 			dwSum[0] /= dwPixels;
 | |
| 			dwSum[1] /= dwPixels;
 | |
| 			dwSum[2] /= dwPixels;
 | |
| 
 | |
| 			usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 0, cAdjust );
 | |
| 			usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 1, cAdjust );
 | |
| 			usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 2, cAdjust );
 | |
| 
 | |
|             DBG( _DBG_INFO2, "RedExpect   = %u\n", bExpect[0] );
 | |
|             DBG( _DBG_INFO2, "GreenExpect = %u\n", bExpect[1] );
 | |
|             DBG( _DBG_INFO2, "BlueExpect  = %u\n", bExpect[2] );
 | |
| 
 | |
| 		} else {
 | |
| 			dwSum[0] = 0;
 | |
| 
 | |
| 			for( dw = 0; dw < dwPixels; dw++ ) {
 | |
| #ifndef SWAP_COARSE
 | |
| 				dwSum[0] += (u_long)_HILO2WORD(((HiLoDef*)scanbuf)[dw]);
 | |
| #else
 | |
| 				dwSum[0] += ((u_short*)scanbuf)[dw];
 | |
| #endif
 | |
| 			}
 | |
| 			dwSum [0] /= dwPixels;
 | |
| 			usb_GetNewOffset( dev, dwSum, dwDiff, cOffset, bExpect, 0, cAdjust );
 | |
| 			regs[0x3a] = regs[0x39] = regs[0x38];
 | |
| 
 | |
| 			DBG(_DBG_INFO2,"Sum = %lu, ave = %lu\n",dwSum[0],dwSum[0]/dwPixels);
 | |
| 			DBG(_DBG_INFO2,"Expect = %u\n", bExpect[0]);
 | |
| 		}
 | |
| 
 | |
| 		_UIO(sanei_lm983x_write(dev->fd, 0x38, ®s[0x38], 3, SANE_TRUE));
 | |
| 		cAdjust >>= 1;
 | |
| 	}
 | |
| 
 | |
| 	if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 		regs[0x38] = bExpect[0];
 | |
| 		regs[0x39] = bExpect[1];
 | |
| 		regs[0x3a] = bExpect[2];
 | |
| 	} else {
 | |
| 
 | |
| 		regs[0x38] = regs[0x39] = regs[0x3a] = bExpect[0];	
 | |
| 	}
 | |
| 
 | |
| 	DBG( _DBG_INFO2, "REG[0x38] = %u\n", regs[0x38] );
 | |
| 	DBG( _DBG_INFO2, "REG[0x39] = %u\n", regs[0x39] );
 | |
| 	DBG( _DBG_INFO2, "REG[0x3a] = %u\n", regs[0x3a] );
 | |
| 	DBG( _DBG_INFO, "usb_AdjustOffset() done.\n" );
 | |
| 
 | |
| 	/* switch it on again on CIS based scanners */
 | |
| 	if( usb_IsCISDevice(dev)) {
 | |
| 
 | |
| 		if( dev->usbDev.pSource->DarkShadOrgY < 0 ) {
 | |
| 			regs[0x29] = hw->bReg_0x29;
 | |
| 			usb_switchLamp( dev, SANE_TRUE );
 | |
| 			usbio_WriteReg( dev->fd, 0x29, regs[0x29]);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /** this function tries to find out some suitable values for the dark
 | |
|  *  fine calibration. If the device owns a black calibration strip
 | |
|  *  the data is simply copied. If not, then the white strip is read 
 | |
|  *  with the lamp switched off...
 | |
|  */
 | |
| static void usb_GetDarkShading( Plustek_Device *dev, u_short *pwDest,
 | |
|                                 HiLoDef *pSrce, u_long dwPixels,
 | |
|                                 u_long dwAdd, int iOffset )
 | |
| {
 | |
| 	u_long    dw;
 | |
| 	u_long    dwSum[2];
 | |
| 	DCapsDef *scaps = &dev->usbDev.Caps;
 | |
| 	HWDef    *hw    = &dev->usbDev.HwSetting;
 | |
| 
 | |
| 	if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
 | |
| 
 | |
| 		u_short w;
 | |
| 		int     wtmp;
 | |
| 
 | |
| 		/* here we use the source  buffer + a static offset */
 | |
| 		for (dw = 0; dw < dwPixels; dw++, pSrce += dwAdd)
 | |
| 		{
 | |
| #ifndef SWAP_FINE
 | |
| 			wtmp = ((int)_PHILO2WORD(pSrce) + iOffset);
 | |
| #else
 | |
| 			wtmp = ((int)_PLOHI2WORD(pSrce) + iOffset);
 | |
| #endif
 | |
| 			if( wtmp < 0 )
 | |
| 				wtmp = 0;
 | |
| 
 | |
| 			if( wtmp > 0xffff )
 | |
| 				wtmp = 0xffff;
 | |
| 
 | |
| 			w = (u_short)wtmp;
 | |
| 
 | |
| #ifndef SWAP_FINE
 | |
| 			pwDest[dw] = _LOBYTE(w) * 256 + _HIBYTE(w);
 | |
| #else
 | |
| 			pwDest[dw] = w;
 | |
| #endif
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		dwSum[0] = dwSum[1] = 0;
 | |
| 		if( hw->bSensorConfiguration & 0x04 ) {
 | |
| 
 | |
| 			/* Even/Odd CCD */
 | |
| 			for( dw = 0; dw < dwPixels; dw++, pSrce += dwAdd ) {
 | |
| #ifndef SWAP_FINE
 | |
| 				dwSum[dw & 1] += (u_long)_PHILO2WORD(pSrce);
 | |
| #else
 | |
| 				dwSum[dw & 1] += (u_long)_PLOHI2WORD(pSrce);
 | |
| #endif
 | |
| 			}
 | |
| 			dwSum[0] /= ((dwPixels + 1UL) >> 1);
 | |
| 			dwSum[1] /= (dwPixels >> 1);
 | |
| 
 | |
| 			if( /*Registry.GetEvenOdd() == 1 ||*/ scaps->bPCB == 2)
 | |
| 			{
 | |
| 				dwSum[0] = dwSum[1] = (dwSum[0] + dwSum[1]) / 2;
 | |
| 			}
 | |
| 
 | |
| 			dwSum[0] = (int)dwSum[0] + iOffset;
 | |
| 			dwSum[1] = (int)dwSum[1] + iOffset;
 | |
| 
 | |
| 			if((int)dwSum[0] < 0)
 | |
| 				dwSum[0] = 0;
 | |
| 
 | |
| 			if((int)dwSum[1] < 0)
 | |
| 				dwSum[1] = 0;
 | |
| #ifndef SWAP_FINE
 | |
| 			dwSum[0] = (u_long)_LOBYTE(_LOWORD(dwSum[0])) * 256UL +
 | |
| 			                             _HIBYTE(_LOWORD(dwSum[0]));
 | |
| 			dwSum[1] = (u_long)_LOBYTE(_LOWORD(dwSum[1])) * 256UL +
 | |
| 			                             _HIBYTE(_LOWORD(dwSum[1]));
 | |
| #else
 | |
| 			dwSum[0] = (u_long)_LOWORD(dwSum[0]);
 | |
| 			dwSum[1] = (u_long)_LOWORD(dwSum[1]);
 | |
| #endif
 | |
| 
 | |
| 			for( dw = 0; dw < dwPixels; dw++ )
 | |
| 				pwDest[dw] = (u_short)dwSum[dw & 1];
 | |
| 		} else {
 | |
| 			
 | |
| 			/* Standard CCD */
 | |
| 
 | |
| 			/* do some averaging on the line */
 | |
| 			for( dw = 0; dw < dwPixels; dw++, pSrce += dwAdd ) {
 | |
| #ifndef SWAP_FINE
 | |
| 				dwSum[0] += (u_long)_PHILO2WORD(pSrce);
 | |
| #else
 | |
| 				dwSum[0] += (u_long)_PLOHI2WORD(pSrce);
 | |
| #endif
 | |
| 			}
 | |
| 
 | |
| 			dwSum[0] /= dwPixels;
 | |
| 
 | |
| 			/* add our offset... */
 | |
| 			dwSum[0] = (int)dwSum[0] + iOffset;
 | |
| 			if((int)dwSum[0] < 0)
 | |
| 				dwSum[0] = 0;
 | |
| #ifndef SWAP_FINE
 | |
| 			dwSum[0] = (u_long)_LOBYTE(_LOWORD(dwSum[0])) * 256UL +
 | |
| 			                             _HIBYTE(_LOWORD(dwSum[0]));
 | |
| #else
 | |
| 			dwSum[0] = (u_long)_LOWORD(dwSum[0]);
 | |
| #endif
 | |
| 
 | |
| 			/* fill the shading data */
 | |
| 			for( dw = 0; dw < dwPixels; dw++ )
 | |
| 				pwDest[dw] = (u_short)dwSum[0];
 | |
| 		}
 | |
| 	}
 | |
| #ifdef SWAP_FINE
 | |
| 	if(usb_HostSwap())
 | |
| 		usb_Swap( pwDest, dwPixels *2 );
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /** usb_AdjustDarkShading
 | |
|  * fine calibration part 1 - read the black calibration area and write
 | |
|  * the black line data to the offset coefficient data in Merlins' DRAM
 | |
|  * If there's no black line available, we can use the min pixel value
 | |
|  * from coarse calibration...
 | |
|  */
 | |
| static SANE_Bool usb_AdjustDarkShading( Plustek_Device *dev )
 | |
| {
 | |
| 	char      tmp[40];
 | |
| 	ScanDef  *scanning = &dev->scanning;
 | |
| 	DCapsDef *scaps    = &dev->usbDev.Caps;
 | |
| 	HWDef    *hw       = &dev->usbDev.HwSetting;
 | |
| 	u_long   *scanbuf  = scanning->pScanBuffer;
 | |
| 	u_char   *regs     = dev->usbDev.a_bRegs;
 | |
| 
 | |
| 	if( usb_IsEscPressed())
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	if( scaps->workaroundFlag & _WAF_SKIP_FINE )
 | |
| 		return SANE_TRUE;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "#########################\n" );
 | |
| 	DBG( _DBG_INFO, "usb_AdjustDarkShading()\n" );
 | |
| 	DBG( _DBG_INFO2, "* MCLK = %f (scanparam-MCLK=%f)\n",
 | |
| 	                    dMCLK, scanning->sParam.dMCLK );
 | |
| 
 | |
| 	usb_PrepareFineCal( dev, &m_ScanParam, 0 );
 | |
| 
 | |
| 	m_ScanParam.Size.dwLines = 1;				/* for gain */
 | |
| 	m_ScanParam.bCalibration = PARAM_DarkShading;
 | |
| 
 | |
| 	if( _LM9831 == hw->chip ) {
 | |
| 
 | |
| 		m_ScanParam.UserDpi.x = usb_SetAsicDpiX( dev, m_ScanParam.UserDpi.x);
 | |
| 		if( m_ScanParam.UserDpi.x < 100)
 | |
| 			m_ScanParam.UserDpi.x = 150;
 | |
| 
 | |
| 		/* Now DPI X is physical */
 | |
| 		m_ScanParam.Origin.x      = m_ScanParam.Origin.x %
 | |
| 		                            (u_short)m_dHDPIDivider;
 | |
| 		m_ScanParam.Size.dwPixels = (u_long)scaps->Normal.Size.x *
 | |
| 		                                    m_ScanParam.UserDpi.x / 300UL;
 | |
| 		m_ScanParam.Size.dwBytes  = m_ScanParam.Size.dwPixels *
 | |
| 		                            2UL * m_ScanParam.bChannels;
 | |
| 		m_dwPixels = scanning->sParam.Size.dwPixels *
 | |
| 		             m_ScanParam.UserDpi.x / scanning->sParam.UserDpi.x;
 | |
| 
 | |
| 		if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
 | |
| 			m_ScanParam.Size.dwBytes *= 3;
 | |
|     }
 | |
| 
 | |
| 	/* if we have dark shading strip, there's no need to switch
 | |
| 	 * the lamp off
 | |
| 	 */
 | |
| 	if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
 | |
| 
 | |
| 		usb_ModuleToHome( dev, SANE_TRUE );
 | |
| 		usb_ModuleMove  ( dev, MOVE_Forward,
 | |
| 		                  (u_long)dev->usbDev.pSource->DarkShadOrgY );
 | |
| 	} else {
 | |
| 
 | |
| 	 	/* switch lamp off to read dark data... */
 | |
| 		regs[0x29] = 0;
 | |
| 		usb_switchLamp( dev, SANE_FALSE );
 | |
| 	}
 | |
| 
 | |
| 	usb_SetScanParameters( dev, &m_ScanParam );
 | |
| 
 | |
| 	if((!usb_ScanBegin(dev, SANE_FALSE)) ||
 | |
| 	   (!usb_ScanReadImage(dev,scanbuf, m_ScanParam.Size.dwPhyBytes)) ||
 | |
| 	   (!usb_ScanEnd( dev ))) {
 | |
| 
 | |
| 		/* on error, reset the lamp settings*/
 | |
| 		regs[0x29] = hw->bReg_0x29;
 | |
|  		usb_switchLamp( dev, SANE_TRUE );
 | |
| 		usbio_WriteReg( dev->fd, 0x29, regs[0x29] );
 | |
| 
 | |
| 		DBG( _DBG_ERROR, "usb_AdjustDarkShading() failed\n" );
 | |
| 		return SANE_FALSE;
 | |
| 	}
 | |
| 	
 | |
| 	/* set illumination mode and switch lamp on again
 | |
| 	 */
 | |
| 	regs[0x29] = hw->bReg_0x29;
 | |
| 	usb_switchLamp( dev, SANE_TRUE );
 | |
| 
 | |
| 	if( !usbio_WriteReg( dev->fd, 0x29, regs[0x29])) {
 | |
| 		DBG( _DBG_ERROR, "usb_AdjustDarkShading() failed\n" );
 | |
| 		return SANE_FALSE;
 | |
| 	}
 | |
| 
 | |
| #ifdef SWAP_FINE
 | |
| 	if(usb_HostSwap())
 | |
| 		usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
 | |
| #endif
 | |
| 
 | |
| 	sprintf( tmp, "fine-black.raw" );
 | |
| 
 | |
| 	dumpPicInit(&m_ScanParam, tmp);
 | |
| 	dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0);
 | |
| 
 | |
| 	usleep(500 * 1000);    /* Warm up lamp again */
 | |
| 
 | |
| 	if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 		if( usb_IsCISDevice(dev)) {
 | |
| 
 | |
| 			usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
 | |
| 			                    m_ScanParam.Size.dwPhyPixels, 1,
 | |
| 			                    scanning->sParam.swOffset[0]);
 | |
| 
 | |
| 			usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
 | |
| 			                    (HiLoDef*)scanbuf + m_ScanParam.Size.dwPhyPixels,
 | |
| 			                     m_ScanParam.Size.dwPhyPixels, 1, scanning->sParam.swOffset[1]);
 | |
| 
 | |
| 			usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
 | |
| 			               (HiLoDef*)scanbuf + m_ScanParam.Size.dwPhyPixels * 2,
 | |
| 			               m_ScanParam.Size.dwPhyPixels, 1, scanning->sParam.swOffset[2]);
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
 | |
| 			                    m_ScanParam.Size.dwPhyPixels, 3,
 | |
| 			                    scanning->sParam.swOffset[0]);
 | |
| 			usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
 | |
| 			                (HiLoDef*)scanbuf + 1, m_ScanParam.Size.dwPhyPixels,
 | |
| 			                3, scanning->sParam.swOffset[1]);
 | |
| 			usb_GetDarkShading( dev, a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
 | |
| 			               (HiLoDef*)scanbuf + 2, m_ScanParam.Size.dwPhyPixels,
 | |
| 			               3, scanning->sParam.swOffset[2]);
 | |
| 		}
 | |
| 	} else {
 | |
| 
 | |
| 		usb_GetDarkShading( dev, a_wDarkShading, (HiLoDef*)scanbuf,
 | |
| 		                    m_ScanParam.Size.dwPhyPixels, 1,
 | |
| 		                    scanning->sParam.swOffset[1]);
 | |
| 
 | |
| 		memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels,
 | |
| 		        a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
 | |
| 		memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2,
 | |
| 		        a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 );
 | |
| 	}
 | |
| 
 | |
| 	regs[0x45] |= 0x10;
 | |
| 
 | |
| 	usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
 | |
|                           scanning->sParam.bDataType == SCANDATATYPE_Color?1:0);
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /** function to remove the brightest values out of each row
 | |
|  * @param dev           - the almighty device structure.
 | |
|  * @param sp            - is a pointer to the scanparam structure used for
 | |
|  *                        scanning the shading lines.
 | |
|  * @param hilight       - defines the number of values to skip.
 | |
|  * @param shading_lines - defines the overall number of shading lines.
 | |
|  */
 | |
| static void usb_CalSortHighlight( Plustek_Device *dev, ScanParam *sp, 
 | |
|                                   u_long hilight, u_long shading_lines )
 | |
| {
 | |
|     ScanDef      *scan = &dev->scanning;
 | |
| 	u_short       r, g, b;
 | |
| 	u_long        lines, w, x;
 | |
| 	RGBUShortDef *pw, *rgb;
 | |
| 
 | |
| 	if( hilight == 0 )
 | |
| 		return;
 | |
| 
 | |
| 	rgb = (RGBUShortDef*)scan->pScanBuffer;
 | |
| 
 | |
| 	/* do it for all relevant lines */
 | |
| 	for( lines = hilight,  rgb = rgb + sp->Size.dwPhyPixels * lines;
 | |
| 	     lines < shading_lines; lines++, rgb += sp->Size.dwPhyPixels ) {
 | |
| 
 | |
| 		/* scan the complete line */
 | |
| 		for( x = 0; x < sp->Size.dwPhyPixels; x++ ) {
 | |
| 
 | |
| 			/* reference is the first scanline */
 | |
| 			pw = (RGBUShortDef*)scan->pScanBuffer;
 | |
| 			r = rgb[x].Red;
 | |
| 			g = rgb[x].Green;
 | |
| 			b = rgb[x].Blue;
 | |
| 
 | |
| 			for( w = 0; w < hilight; w++, pw += sp->Size.dwPhyPixels ) {
 | |
| 
 | |
| 				if( r > pw[x].Red )
 | |
| 					_SWAP( r, pw[x].Red );
 | |
| 
 | |
| 				if( g > pw[x].Green )
 | |
| 					_SWAP( g, pw[x].Green );
 | |
| 
 | |
| 				if( b > pw[x].Blue )
 | |
| 					_SWAP( b, pw[x].Blue );
 | |
| 			}
 | |
| 			rgb[x].Red   = r;
 | |
| 			rgb[x].Green = g;
 | |
| 			rgb[x].Blue  = b;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** function to remove the brightest values out of each row
 | |
|  * @param dev           - the almighty device structure.
 | |
|  * @param sp            - is a pointer to the scanparam structure used for 
 | |
|  *                        scanning the shading lines.
 | |
|  * @param hilight       - defines the number of values to skip.
 | |
|  * @param shading_lines - defines the overall number of shading lines.
 | |
|  */
 | |
| static void usb_CalSortShadow( Plustek_Device *dev, ScanParam *sp,
 | |
|                                u_long hilight, u_long shadow, u_long shading_lines )
 | |
| {
 | |
| 	ScanDef      *scan = &dev->scanning;
 | |
| 	u_short       r, g, b;
 | |
| 	u_long        lines, w, x;
 | |
| 	RGBUShortDef *pw, *rgb;
 | |
| 
 | |
| 	if( shadow == 0 )
 | |
| 		return;
 | |
| 
 | |
| 	rgb = (RGBUShortDef*)scan->pScanBuffer;
 | |
| 
 | |
| 	for( lines = hilight,  rgb = rgb + sp->Size.dwPhyPixels * lines;
 | |
| 	     lines < shading_lines-shadow; lines++, rgb += sp->Size.dwPhyPixels ) {
 | |
| 
 | |
| 		for (x = 0; x < sp->Size.dwPhyPixels; x++) {
 | |
| 
 | |
| 			pw = ((RGBUShortDef*)scan->pScanBuffer) + (shading_lines - shadow) *
 | |
| 			                                           sp->Size.dwPhyPixels;
 | |
| 			r = rgb[x].Red;
 | |
| 			g = rgb[x].Green;
 | |
| 			b = rgb[x].Blue;
 | |
| 
 | |
| 			for( w = 0; w < shadow; w++, pw += sp->Size.dwPhyPixels ) {
 | |
| 				if( r < pw[x].Red )
 | |
| 					_SWAP( r, pw[x].Red );
 | |
| 				if( g < pw[x].Green )
 | |
| 					_SWAP( g, pw [x].Green );
 | |
| 				if( b > pw[x].Blue )
 | |
| 					_SWAP( b, pw[x].Blue );
 | |
| 			}
 | |
| 			rgb[x].Red   = r;
 | |
| 			rgb[x].Green = g;
 | |
| 			rgb[x].Blue  = b;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void usb_procHighlightAndShadow( Plustek_Device *dev, ScanParam *sp,
 | |
|                                         u_long hilight, u_long shadow, u_long shading_lines )
 | |
| {
 | |
| 	ScanDef      *scan = &dev->scanning;
 | |
| 	u_long        lines, x;
 | |
| 	u_long       *pr, *pg, *pb;
 | |
| 	RGBUShortDef *rgb;
 | |
| 
 | |
| 	pr = (u_long*)((u_char*)scan->pScanBuffer + sp->Size.dwPhyBytes * shading_lines);
 | |
| 	pg = pr + sp->Size.dwPhyPixels;
 | |
| 	pb = pg + sp->Size.dwPhyPixels;
 | |
| 
 | |
| 	memset(pr, 0, sp->Size.dwPhyPixels * 4UL * 3UL);
 | |
| 
 | |
| 	/* Sort hilight */
 | |
| 	usb_CalSortHighlight(dev, sp, hilight, shading_lines);
 | |
| 
 | |
| 	/* Sort shadow */
 | |
| 	usb_CalSortShadow(dev, sp, hilight, shadow, shading_lines);
 | |
| 
 | |
| 	rgb  = (RGBUShortDef*)scan->pScanBuffer;
 | |
| 	rgb += sp->Size.dwPhyPixels * hilight;
 | |
| 
 | |
| 	/* Sum */
 | |
| 	for( lines = hilight; lines < (shading_lines-shadow); lines++ ) {
 | |
| 
 | |
| 		for( x = 0; x < sp->Size.dwPhyPixels; x++ ) {
 | |
| 			pr[x] += rgb[x].Red;
 | |
| 			pg[x] += rgb[x].Green;
 | |
| 			pb[x] += rgb[x].Blue;
 | |
| 		}
 | |
| 
 | |
| 		rgb += sp->Size.dwPhyPixels;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** usb_AdjustWhiteShading
 | |
|  * fine calibration part 2 - read the white calibration area and calculate
 | |
|  * the gain coefficient for each pixel
 | |
|  */
 | |
| static SANE_Bool usb_AdjustWhiteShading( Plustek_Device *dev )
 | |
| {
 | |
| 	char          tmp[40];
 | |
| 	ScanDef      *scan  = &dev->scanning;
 | |
| 	DCapsDef     *scaps = &dev->usbDev.Caps;
 | |
| 	HWDef        *hw    = &dev->usbDev.HwSetting;
 | |
| 	u_long       *pBuf  = scan->pScanBuffer;
 | |
| 	u_long        dw, dwLines, dwRead;
 | |
| 	u_long        shading_lines;
 | |
| 	MonoWordDef  *pValue;
 | |
| 	u_short      *m_pAvMono;
 | |
| 	u_long       *pdw, *m_pSum;
 | |
| 	u_short       hilight, shadow;
 | |
| 	int           i;
 | |
| 	SANE_Bool     swap = usb_HostSwap();
 | |
| 	
 | |
| 	if( scaps->workaroundFlag & _WAF_SKIP_FINE )
 | |
| 		return SANE_TRUE;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "#########################\n" );
 | |
| 	DBG( _DBG_INFO, "usb_AdjustWhiteShading()\n" );
 | |
| 	
 | |
| 	m_pAvMono = (u_short*)scan->pScanBuffer;
 | |
| 
 | |
| 	if( usb_IsEscPressed())
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	usb_PrepareFineCal( dev, &m_ScanParam, 0 );
 | |
| 
 | |
| 	if( m_ScanParam.PhyDpi.x > 75)
 | |
| 		shading_lines = 64;
 | |
| 	else
 | |
| 		shading_lines = 32;
 | |
| 
 | |
| 	/* NOTE: hilight + shadow < shading_lines */
 | |
| 	hilight = 4;
 | |
| 	shadow  = 4;
 | |
| 
 | |
| 	m_ScanParam.bCalibration = PARAM_WhiteShading;
 | |
| 	m_ScanParam.Size.dwLines = shading_lines;
 | |
| 
 | |
| 	if( _LM9831 == hw->chip ) {
 | |
| 
 | |
| 		m_ScanParam.UserDpi.x = usb_SetAsicDpiX( dev, m_ScanParam.UserDpi.x);
 | |
| 		if( m_ScanParam.UserDpi.x < 100 )
 | |
| 			m_ScanParam.UserDpi.x = 150;
 | |
| 
 | |
| 		/* Now DPI X is physical */
 | |
| 		m_ScanParam.Origin.x      = m_ScanParam.Origin.x % (u_short)m_dHDPIDivider;
 | |
| 		m_ScanParam.Size.dwPixels = (u_long)scaps->Normal.Size.x * m_ScanParam.UserDpi.x / 300UL;
 | |
| 		m_ScanParam.Size.dwBytes  = m_ScanParam.Size.dwPixels * 2UL * m_ScanParam.bChannels;
 | |
| 		if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color )
 | |
| 			m_ScanParam.Size.dwBytes *= 3;
 | |
| 
 | |
| 		m_dwPixels = scan->sParam.Size.dwPixels * m_ScanParam.UserDpi.x / 
 | |
| 		             scan->sParam.UserDpi.x;
 | |
| 
 | |
| 		dw = (u_long)(hw->wDRAMSize - 196 /*192 KiB*/) * 1024UL;
 | |
| 		for( dwLines = dw / m_ScanParam.Size.dwBytes;
 | |
| 			 dwLines < m_ScanParam.Size.dwLines; m_ScanParam.Size.dwLines>>=1);
 | |
| 	}
 | |
| 
 | |
| 	/* goto the correct position again... */
 | |
| 	if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) {
 | |
| 
 | |
| 		usb_ModuleToHome( dev, SANE_TRUE );
 | |
| 		usb_ModuleMove  ( dev, MOVE_Forward,
 | |
| 		                  (u_long)dev->usbDev.pSource->ShadingOriginY );
 | |
| 	}
 | |
| 
 | |
| 	sprintf( tmp, "fine-white.raw" );
 | |
| 	DBG( _DBG_INFO2, "FINE WHITE Calibration Strip: %s\n", tmp );
 | |
| 	DBG( _DBG_INFO2, "Shad.-Lines = %lu\n", shading_lines );
 | |
| 	DBG( _DBG_INFO2, "Lines       = %lu\n", m_ScanParam.Size.dwLines  );
 | |
| 	DBG( _DBG_INFO2, "Pixels      = %lu\n", m_ScanParam.Size.dwPixels );
 | |
| 	DBG( _DBG_INFO2, "Bytes       = %lu\n", m_ScanParam.Size.dwBytes  );
 | |
| 	DBG( _DBG_INFO2, "Origin.X    = %u\n",  m_ScanParam.Origin.x );
 | |
| 
 | |
| 	for( dw = shading_lines, dwRead = 0; dw; dw -= m_ScanParam.Size.dwLines ) {
 | |
| 
 | |
| 		if( usb_SetScanParameters( dev, &m_ScanParam ) &&
 | |
| 		    usb_ScanBegin( dev, SANE_FALSE )) {
 | |
| 
 | |
| 			DBG(_DBG_INFO2,"TotalBytes = %lu\n",m_ScanParam.Size.dwTotalBytes);
 | |
| 			if( _LM9831 == hw->chip ) {
 | |
| 				/* Delay for white shading hold for 9831-1200 scanner */
 | |
| 				usleep(900000);
 | |
| 			}
 | |
| 
 | |
| 			if( usb_ScanReadImage( dev, (u_char*)pBuf + dwRead,
 | |
| 			                       m_ScanParam.Size.dwTotalBytes)) {
 | |
| 
 | |
| 				if( _LM9831 == hw->chip ) {
 | |
| 					/* Delay for white shading hold for 9831-1200 scanner */
 | |
| 					usleep(10000);
 | |
| 				}
 | |
| 
 | |
| 				if( 0 == dwRead ) {
 | |
| 					dumpPicInit(&m_ScanParam, tmp);
 | |
| 				}
 | |
| 				
 | |
| 				dumpPic(tmp, (u_char*)pBuf + dwRead, m_ScanParam.Size.dwTotalBytes, 0);
 | |
| 
 | |
| 				if( usb_ScanEnd( dev )) {
 | |
| 					dwRead += m_ScanParam.Size.dwTotalBytes;
 | |
| 					continue;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		DBG( _DBG_ERROR, "usb_AdjustWhiteShading() failed\n" );
 | |
| 		return SANE_FALSE;
 | |
| 	}
 | |
| 
 | |
| 	m_pSum = (u_long*)((u_char*)pBuf + m_ScanParam.Size.dwPhyBytes * shading_lines);
 | |
| 	
 | |
| 	/*
 | |
| 	 * do some reordering on CIS based devices:
 | |
| 	 * from RRRRRRR.... GGGGGGGG.... BBBBBBBBB, create RGB RGB RGB ...
 | |
| 	 * to use the following code, originally written for CCD devices...
 | |
| 	 */
 | |
| 	if( usb_IsCISDevice(dev)) {
 | |
| 
 | |
| 		u_short *dest, *src;
 | |
| 		u_long   dww;
 | |
| 
 | |
| 		src = (u_short*)pBuf;
 | |
| 
 | |
| 		DBG( _DBG_INFO2, "PhyBytes  = %lu\n", m_ScanParam.Size.dwPhyBytes );
 | |
| 		DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels );
 | |
| 		DBG( _DBG_INFO2, "Pixels    = %lu\n", m_ScanParam.Size.dwPixels );
 | |
| 		DBG( _DBG_INFO2, "Bytes     = %lu\n", m_ScanParam.Size.dwBytes  );
 | |
| 		DBG( _DBG_INFO2, "Channels  = %u\n",  m_ScanParam.bChannels );
 | |
| 
 | |
| 		for( dwLines = shading_lines; dwLines; dwLines-- ) {
 | |
| 
 | |
| 			dest = a_wWhiteShading;
 | |
| 
 | |
| 			for( dw=dww=0; dw < m_ScanParam.Size.dwPhyPixels; dw++, dww+=3 ) {
 | |
| 
 | |
| 				dest[dww]     = src[dw];
 | |
| 				dest[dww + 1] = src[m_ScanParam.Size.dwPhyPixels + dw];
 | |
| 				dest[dww + 2] = src[m_ScanParam.Size.dwPhyPixels * 2 + dw];
 | |
| 			}
 | |
| 
 | |
| 			/* copy line back ... */
 | |
| 			memcpy( src, dest, m_ScanParam.Size.dwPhyPixels * 3 * 2 );
 | |
| 			src = &src[m_ScanParam.Size.dwPhyPixels * 3];
 | |
| 		}
 | |
| 
 | |
| 		m_ScanParam.bChannels = 3;
 | |
| 	}
 | |
| 
 | |
| 	if( _LM9831 == hw->chip ) {
 | |
| 
 | |
| 		u_short *pwDest = (u_short*)pBuf;
 | |
| 		HiLoDef *pwSrce = (HiLoDef*)pBuf;
 | |
| 
 | |
| 		pwSrce  += ((u_long)(scan->sParam.Origin.x-m_ScanParam.Origin.x) /
 | |
| 		           (u_short)m_dHDPIDivider) *
 | |
| 		                   (scaps->OpticDpi.x / 300UL) * m_ScanParam.bChannels;
 | |
| 
 | |
| 		for( dwLines = shading_lines; dwLines; dwLines--) {
 | |
| 
 | |
| #ifdef SWAP_FINE
 | |
| 			if(usb_HostSwap()) {
 | |
| #endif
 | |
| 				for( dw = 0; dw < m_dwPixels * m_ScanParam.bChannels; dw++ ) 
 | |
| 					pwDest[dw] = _HILO2WORD(pwSrce[dw]);
 | |
| #ifdef SWAP_FINE
 | |
| 			} else {
 | |
| 				for( dw = 0; dw < m_dwPixels * m_ScanParam.bChannels; dw++ )
 | |
| 					pwDest[dw] = _LOHI2WORD(pwSrce[dw]);
 | |
| 			}
 | |
| #endif
 | |
| 			pwDest += (u_long)m_dwPixels * m_ScanParam.bChannels;
 | |
| 			pwSrce  = (HiLoDef*)((u_char*)pwSrce + m_ScanParam.Size.dwPhyBytes);
 | |
| 		}
 | |
| 
 | |
| 		_SWAP(m_ScanParam.Size.dwPhyPixels, m_dwPixels);
 | |
| 	} else {
 | |
| 		/* Discard the status word and conv. the hi-lo order to intel format */
 | |
| 		u_short *pwDest = (u_short*)pBuf;
 | |
| 		HiLoDef *pwSrce = (HiLoDef*)pBuf;
 | |
| 
 | |
| 		for( dwLines = shading_lines; dwLines; dwLines-- ) {
 | |
| 
 | |
| #ifdef SWAP_FINE
 | |
| 			if(usb_HostSwap()) {
 | |
| #endif
 | |
| 				for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels *
 | |
| 				                                 m_ScanParam.bChannels; dw++) {
 | |
| 					pwDest[dw] = _HILO2WORD(pwSrce[dw]);
 | |
| 				}
 | |
| #ifdef SWAP_FINE
 | |
| 			} else {
 | |
| 				for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels *
 | |
| 				                                 m_ScanParam.bChannels; dw++) {
 | |
| 					pwDest[dw] = _LOHI2WORD(pwSrce[dw]);
 | |
| 				}
 | |
| 			}
 | |
| #endif
 | |
| 			pwDest += m_ScanParam.Size.dwPhyPixels * m_ScanParam.bChannels;
 | |
| 			pwSrce = (HiLoDef*)((u_char*)pwSrce + m_ScanParam.Size.dwPhyBytes);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 		usb_procHighlightAndShadow(dev, &m_ScanParam, hilight, shadow, shading_lines);
 | |
| 
 | |
| 		pValue = (MonoWordDef*)a_wWhiteShading;
 | |
| 		pdw    = (u_long*)m_pSum;
 | |
| 
 | |
| 		/* Software gain */
 | |
| 		if( scan->sParam.bSource != SOURCE_Negative ) {
 | |
| 
 | |
| 			for( i = 0; i < 3; i++ ) {
 | |
| 
 | |
| 				for(dw=m_ScanParam.Size.dwPhyPixels; dw; dw--,pValue++,pdw++) {
 | |
| 
 | |
| 					*pdw = *pdw * 1000 / ((shading_lines - hilight - shadow) *
 | |
| 					                               scan->sParam.swGain[i]);
 | |
| 					if(*pdw > 65535U)
 | |
| 						pValue->Mono = 65535U;
 | |
| 					else
 | |
| 						pValue->Mono = (u_short)*pdw;
 | |
| 					
 | |
| 					if (pValue->Mono > 16384U)
 | |
| 						pValue->Mono = (u_short)(GAIN_Target * 16384U / pValue->Mono);
 | |
| 					else
 | |
| 						pValue->Mono = GAIN_Target;
 | |
| 
 | |
| #ifdef SWAP_FINE
 | |
| 					if( swap )
 | |
| #endif
 | |
| 						_SWAP(pValue->HiLo.bHi, pValue->HiLo.bLo);
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			for( dw = m_ScanParam.Size.dwPhyPixels*3; dw; dw--,pValue++,pdw++)
 | |
| 				pValue->Mono=(u_short)(*pdw/(shading_lines-hilight-shadow));
 | |
| 
 | |
| 				/* swapping will be done later in usb_ResizeWhiteShading() */
 | |
| 		}
 | |
| 	} else {
 | |
| 
 | |
| 		/* gray mode */
 | |
| 		u_short *pwAv, *pw;
 | |
| 		u_short  w, wV;
 | |
| 
 | |
| 		memset( m_pSum, 0, m_ScanParam.Size.dwPhyPixels << 2 );
 | |
| 		if( hilight ) {
 | |
| 			for( dwLines = hilight,
 | |
| 				 pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
 | |
| 				 dwLines < shading_lines;
 | |
|               				 dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels) {
 | |
| 
 | |
| 				for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
 | |
| 
 | |
| 					pw = m_pAvMono;
 | |
| 					wV = pwAv [dw];
 | |
| 					for( w = 0; w < hilight; w++,
 | |
| 						 pw += m_ScanParam.Size.dwPhyPixels ) {
 | |
| 						if( wV > pw[dw] )
 | |
| 							_SWAP( wV, pw[dw] );
 | |
| 					}
 | |
| 					pwAv[dw] = wV;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* Sort shadow */
 | |
| 		if (shadow) {
 | |
| 			for (dwLines = hilight, pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
 | |
| 			 	 dwLines < (shading_lines - shadow); dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels)
 | |
| 				for (dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++)
 | |
| 				{
 | |
| 					pw = m_pAvMono + (shading_lines - shadow) * m_ScanParam.Size.dwPhyPixels;
 | |
| 					wV = pwAv [dw];
 | |
| 					for (w = 0; w < shadow; w++, pw += m_ScanParam.Size.dwPhyPixels)
 | |
| 						if (wV < pw [dw])
 | |
| 							_SWAP (wV, pw[dw]);
 | |
| 					pwAv [dw] = wV;
 | |
| 				}
 | |
| 		}
 | |
| 
 | |
| 		/* Sum */
 | |
| 		pdw = (u_long*)m_pSum;
 | |
| 
 | |
| 		for (dwLines = hilight,
 | |
| 			 pwAv = m_pAvMono + m_ScanParam.Size.dwPhyPixels * dwLines;
 | |
| 			 dwLines < (shading_lines - shadow);
 | |
| 			 dwLines++, pwAv += m_ScanParam.Size.dwPhyPixels) {
 | |
| 			for (dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++)
 | |
| 				pdw[dw] += pwAv[dw];
 | |
| 		}
 | |
| 
 | |
| 		/* Software gain */
 | |
| 		pValue = (MonoWordDef*)a_wWhiteShading;
 | |
| 		if( scan->sParam.bSource != SOURCE_Negative ) {
 | |
| 
 | |
| 			for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++) {
 | |
| 				
 | |
| 				pdw[dw] = pdw[dw] * 1000 /((shading_lines-hilight-shadow) *
 | |
| 				                                 scan->sParam.swGain[1]);
 | |
| 				if( pdw[dw] > 65535U )
 | |
| 					pValue[dw].Mono = 65535;
 | |
| 				else
 | |
| 					pValue[dw].Mono = (u_short)pdw[dw];
 | |
| 
 | |
| 				if( pValue[dw].Mono > 16384U ) {
 | |
| 					pValue[dw].Mono = (u_short)(GAIN_Target * 16384U / pValue[dw].Mono);
 | |
| 				} else {
 | |
| 					pValue[dw].Mono = GAIN_Target;
 | |
| 				}
 | |
| 
 | |
| #ifdef SWAP_FINE
 | |
| 				if( swap )
 | |
| #endif
 | |
| 					_SWAP(pValue[dw].HiLo.bHi, pValue[dw].HiLo.bLo);
 | |
| 			}
 | |
| 			
 | |
| 		} else{
 | |
| 
 | |
| 			for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
 | |
| 				pValue[dw].Mono = (u_short)(pdw[dw] /
 | |
| 				                  (shading_lines - hilight - shadow));
 | |
| 
 | |
| 				/* swapping will be done later in usb_ResizeWhiteShading() */
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	usb_SaveCalSetShading( dev, &m_ScanParam );
 | |
| 
 | |
| 	if( scan->sParam.bSource != SOURCE_Negative ) {
 | |
| 		usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
 | |
| 	                         scan->sParam.bDataType == SCANDATATYPE_Color?1:0);
 | |
| 	}
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /** for negative film only
 | |
|  * we need to resize the gain to obtain bright white...
 | |
|  */
 | |
| static void usb_ResizeWhiteShading( double dAmp, u_short *pwShading, int iGain )
 | |
| {
 | |
| 	u_long  dw, dwAmp;
 | |
| 	u_short w;
 | |
| 	
 | |
| 	DBG( _DBG_INFO2, "ResizeWhiteShading: dAmp=%.3f, iGain=%i\n", dAmp, iGain );
 | |
| 
 | |
| 	for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) {
 | |
| 		
 | |
| 		dwAmp = (u_long)(GAIN_Target * 0x4000 /
 | |
| 		                            (pwShading[dw] + 1) * dAmp) * iGain / 1000;
 | |
| 
 | |
| 		if( dwAmp <= GAIN_Target)
 | |
| 			w = (u_short)dwAmp;
 | |
| 		else 
 | |
| 			w = GAIN_Target;
 | |
| 
 | |
| #ifndef SWAP_FINE
 | |
| 		pwShading[dw] = (u_short)_LOBYTE(w) * 256 + _HIBYTE(w);
 | |
| #else
 | |
| 		pwShading[dw] = w;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| #ifdef SWAP_FINE
 | |
| 	if( usb_HostSwap())
 | |
| 		usb_Swap( pwShading, m_ScanParam.Size.dwPhyPixels );
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /** do the base settings for calibration scans
 | |
|  */
 | |
| static void
 | |
| usb_PrepareCalibration( Plustek_Device *dev )
 | |
| {
 | |
| 	ScanDef  *scan  = &dev->scanning;
 | |
| 	DCapsDef *scaps = &dev->usbDev.Caps;
 | |
| 	u_char   *regs  = dev->usbDev.a_bRegs;
 | |
| 
 | |
| 	usb_GetSWOffsetGain( dev );
 | |
| 
 | |
| 	memset( &m_ScanParam, 0, sizeof(ScanParam));
 | |
| 
 | |
| 	m_ScanParam.UserDpi   = scaps->OpticDpi;
 | |
| 	m_ScanParam.PhyDpi    = scaps->OpticDpi;
 | |
| 	m_ScanParam.bChannels = scan->sParam.bChannels;
 | |
| 	m_ScanParam.bBitDepth = 16;
 | |
| 	m_ScanParam.bSource   = scan->sParam.bSource;
 | |
| 	m_ScanParam.Origin.y  = 0;
 | |
| 
 | |
| 	if( scan->sParam.bDataType == SCANDATATYPE_Color )
 | |
| 		m_ScanParam.bDataType = SCANDATATYPE_Color;
 | |
| 	else
 | |
| 		m_ScanParam.bDataType = SCANDATATYPE_Gray;
 | |
| 
 | |
| 	usb_SetMCLK( dev, &m_ScanParam );
 | |
|  
 | |
| 	/* preset these registers offset/gain */
 | |
| 	regs[0x38] = regs[0x39] = regs[0x3a] = 0;
 | |
| 	regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 	regs[0x45] &= ~0x10;
 | |
| 
 | |
| 	memset( a_wWhiteShading, 0, _SHADING_BUF );
 | |
| 	memset( a_wDarkShading,  0, _SHADING_BUF );
 | |
| 
 | |
| 	scan->skipCoarseCalib = SANE_FALSE;
 | |
| 
 | |
| 	if( dev->adj.cacheCalData )
 | |
| 		if( usb_ReadAndSetCalData( dev ))
 | |
| 			scan->skipCoarseCalib = SANE_TRUE;
 | |
| 
 | |
| 	/* as sheet-fed device we use the cached values, or
 | |
| 	 * perform the calibration upon request
 | |
| 	 */
 | |
| 	if( usb_IsSheetFedDevice(dev)) {
 | |
| 		if( !scan->skipCoarseCalib && !usb_InCalibrationMode(dev)) {
 | |
| 
 | |
| 			DBG(_DBG_INFO2,"SHEET-FED device, skip coarse calibration!\n");
 | |
| 			scan->skipCoarseCalib = SANE_TRUE;
 | |
| 
 | |
| 			regs[0x3b] = 0x0a;
 | |
| 			regs[0x3c] = 0x0a;
 | |
| 			regs[0x3d] = 0x0a;
 | |
| 
 | |
| 			/* use frontend values... */
 | |
| 			if((dev->adj.rofs != -1) &&
 | |
| 			   (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) {
 | |
| 				regs[0x38] = (dev->adj.rofs & 0x3f);
 | |
| 				regs[0x39] = (dev->adj.gofs & 0x3f);
 | |
| 				regs[0x3a] = (dev->adj.bofs & 0x3f);
 | |
| 			}
 | |
| 
 | |
| 			if((dev->adj.rgain != -1) &&
 | |
| 			   (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) {
 | |
| 				setAdjGain( dev->adj.rgain, ®s[0x3b] );
 | |
| 				setAdjGain( dev->adj.ggain, ®s[0x3c] );
 | |
| 				setAdjGain( dev->adj.bgain, ®s[0x3d] );
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  */
 | |
| static SANE_Bool
 | |
| usb_SpeedTest( Plustek_Device *dev )
 | |
| {
 | |
| 	int       i;
 | |
| 	double    s, e, r, tr;
 | |
| 	struct timeval start, end;
 | |
| 	DCapsDef *scaps   = &dev->usbDev.Caps;
 | |
| 	HWDef    *hw      = &dev->usbDev.HwSetting;
 | |
| 	u_char   *regs    = dev->usbDev.a_bRegs;
 | |
| 	u_long   *scanbuf = dev->scanning.pScanBuffer;
 | |
| 
 | |
| 	if( usb_IsEscPressed())
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	bMaxITA = 0xff;
 | |
| 
 | |
| 	DBG( 1, "#########################\n" );
 | |
| 	DBG( 1, "usb_SpeedTest(%d,%lu)\n", dev->initialized, dev->transferRate );
 | |
| 	if( dev->transferRate != DEFAULT_RATE ) {
 | |
| 		DBG( 1, "* skipped, using already detected speed: %lu Bytes/s\n", 
 | |
|                 dev->transferRate );
 | |
| 		return SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
| 	usb_PrepareCalibration( dev );
 | |
| 	regs[0x38] = regs[0x39] = regs[0x3a] = 0;
 | |
| 	regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 	/* define the strip to scan for warming up the lamp, in the end
 | |
| 	 * we always scan the full line, even for TPA
 | |
| 	 */
 | |
| 	m_ScanParam.bDataType     = SCANDATATYPE_Color;
 | |
| 	m_ScanParam.bCalibration  = PARAM_Gain;
 | |
| 	m_ScanParam.dMCLK         = dMCLK;
 | |
| 	m_ScanParam.bBitDepth     = 8;
 | |
| 	m_ScanParam.Size.dwLines  = 1;
 | |
| 	m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
 | |
| 	                            scaps->OpticDpi.x / 300UL;
 | |
| 	m_ScanParam.Size.dwBytes  = m_ScanParam.Size.dwPixels *
 | |
| 	                            2 * m_ScanParam.bChannels;
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev))
 | |
| 		m_ScanParam.Size.dwBytes *= 3;
 | |
| 
 | |
| 	m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
 | |
| 	                                                300UL / scaps->OpticDpi.x);
 | |
| 	r = 0.0;
 | |
| 	dev->transferRate = 2000000;
 | |
| 
 | |
| 	for( i = 0; i < _TLOOPS ; i++ ) {
 | |
| 
 | |
| 		if( !usb_SetScanParameters( dev, &m_ScanParam ))
 | |
| 			return SANE_FALSE;
 | |
| 
 | |
| 		if( !usb_ScanBegin( dev, SANE_FALSE )) {
 | |
| 			DBG( _DBG_ERROR, "usb_SpeedTest() failed\n" );
 | |
| 			return SANE_FALSE;
 | |
| 		}
 | |
| 		if (!usb_IsDataAvailableInDRAM( dev )) 
 | |
| 			return SANE_FALSE;
 | |
| 
 | |
| 		m_fFirst = SANE_FALSE;
 | |
| 		gettimeofday( &start, NULL );
 | |
| 		usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes );
 | |
| 		gettimeofday( &end, NULL );
 | |
| 		usb_ScanEnd( dev );
 | |
| 		s = (double)start.tv_sec * 1000000.0 + (double)start.tv_usec;
 | |
| 		e = (double)end.tv_sec   * 1000000.0 + (double)end.tv_usec;
 | |
| 
 | |
| 		if( e > s )
 | |
| 			r += (e - s);
 | |
| 		else
 | |
| 			r += (s - e);
 | |
| 	}
 | |
| 
 | |
| 	tr = ((double)m_ScanParam.Size.dwPhyBytes * _TLOOPS * 1000000.0)/r;
 | |
| 	dev->transferRate = (u_long)tr;
 | |
| 	DBG( 1, "usb_SpeedTest() done - %u loops, %.4fus --> %.4f B/s, %lu\n", 
 | |
| 	        _TLOOPS, r, tr, dev->transferRate );
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /** read the white calibration strip until the lamp seems to be stable
 | |
|  * the timed warmup will be used, when the warmup time is set to -1
 | |
|  */
 | |
| static SANE_Bool
 | |
| usb_AutoWarmup( Plustek_Device *dev )
 | |
| {
 | |
| 	int       i, stable_count;
 | |
| 	ScanDef  *scanning = &dev->scanning;
 | |
| 	DCapsDef *scaps    = &dev->usbDev.Caps;
 | |
| 	HWDef    *hw       = &dev->usbDev.HwSetting;
 | |
| 	u_long   *scanbuf  = scanning->pScanBuffer;
 | |
| 	u_char   *regs     = dev->usbDev.a_bRegs;
 | |
| 	u_long    dw, start, end, len;
 | |
| 	u_long    curR,   curG,  curB;
 | |
| 	u_long    lastR, lastG, lastB;
 | |
| 	long      diffR, diffG, diffB;
 | |
| 	long      thresh = _AUTO_THRESH;
 | |
| 
 | |
| 	if( usb_IsEscPressed())
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	bMaxITA = 0xff;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "#########################\n" );
 | |
| 	DBG( _DBG_INFO, "usb_AutoWarmup()\n" );
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev)) {
 | |
| 		DBG( _DBG_INFO, "- function skipped, CIS device!\n" );
 | |
| 		return SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
| 	if( dev->adj.warmup >= 0 ) {
 | |
| 		DBG( _DBG_INFO, "- using timed warmup: %ds\n", dev->adj.warmup );
 | |
| 		if( !usb_Wait4Warmup( dev )) {
 | |
| 			DBG( _DBG_ERROR, "- CANCEL detected\n" );
 | |
| 			return SANE_FALSE;
 | |
| 		}
 | |
| 		return SANE_TRUE;
 | |
| 	}
 | |
| 
 | |
| 	usb_PrepareCalibration( dev );
 | |
| 	regs[0x38] = regs[0x39] = regs[0x3a] = 0;
 | |
| 	regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 	/* define the strip to scan for warming up the lamp, in the end
 | |
| 	 * we always scan the full line, even for TPA
 | |
| 	 */
 | |
| 	m_ScanParam.bDataType     = SCANDATATYPE_Color;
 | |
| 	m_ScanParam.bCalibration  = PARAM_Gain;
 | |
| 	m_ScanParam.dMCLK         = dMCLK;
 | |
| 	m_ScanParam.Size.dwLines  = 1;
 | |
| 	m_ScanParam.Size.dwPixels = scaps->Normal.Size.x *
 | |
| 	                            scaps->OpticDpi.x / 300UL;
 | |
| 	m_ScanParam.Size.dwBytes  = m_ScanParam.Size.dwPixels *
 | |
| 	                            2 * m_ScanParam.bChannels;
 | |
| 
 | |
| 	if( usb_IsCISDevice(dev))
 | |
| 		m_ScanParam.Size.dwBytes *= 3;
 | |
| 
 | |
| 	m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart *
 | |
| 	                                                300UL / scaps->OpticDpi.x);
 | |
| 
 | |
| 	stable_count = 0;
 | |
| 	start = 500;
 | |
| 	len   = m_ScanParam.Size.dwPixels;
 | |
| 
 | |
| 	if( scanning->sParam.bSource == SOURCE_Transparency ) {
 | |
| 		start  = scaps->Positive.DataOrigin.x * scaps->OpticDpi.x / 300UL;
 | |
| 		len    = scaps->Positive.Size.x * scaps->OpticDpi.x / 300UL;
 | |
| 		thresh = _AUTO_TPA_THRESH;
 | |
| 	}
 | |
| 	else if( scanning->sParam.bSource == SOURCE_Negative ) {
 | |
| 		start  = scaps->Negative.DataOrigin.x * scaps->OpticDpi.x / 300UL;
 | |
| 		len    = scaps->Negative.Size.x * scaps->OpticDpi.x / 300UL;
 | |
| 		thresh = _AUTO_TPA_THRESH;
 | |
| 	}
 | |
| 	end = start + len;
 | |
| 	DBG( _DBG_INFO2, "Start=%lu, End=%lu, Len=%lu, Thresh=%li\n", 
 | |
| 	                 start, end, len, thresh );
 | |
| 
 | |
| 	lastR = lastG = lastB = 0;
 | |
| 	for( i = 0; i < _MAX_AUTO_WARMUP + 1 ; i++ ) {
 | |
| 
 | |
| 		if( !usb_SetScanParameters( dev, &m_ScanParam ))
 | |
| 			return SANE_FALSE;
 | |
| 
 | |
| 		if( !usb_ScanBegin( dev, SANE_FALSE ) ||
 | |
| 		    !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) ||
 | |
| 		    !usb_ScanEnd( dev )) {
 | |
| 			DBG( _DBG_ERROR, "usb_AutoWarmup() failed\n" );
 | |
| 			return SANE_FALSE;
 | |
| 		}
 | |
| 
 | |
| #ifdef SWAP_COARSE
 | |
| 		if(usb_HostSwap())
 | |
| #endif
 | |
| 			usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes );
 | |
| 
 | |
| 		if( end > m_ScanParam.Size.dwPhyPixels )
 | |
| 			end = m_ScanParam.Size.dwPhyPixels;
 | |
| 
 | |
| 		curR = curG = curB = 0;
 | |
| 		for( dw = start; dw < end; dw++ ) {
 | |
| 			
 | |
| 			if( usb_IsCISDevice(dev)) {
 | |
| 				curR += ((u_short*)scanbuf)[dw];
 | |
| 				curG += ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1];
 | |
| 				curB += ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2];
 | |
| 			} else {
 | |
| 				curR += ((RGBUShortDef*)scanbuf)[dw].Red;
 | |
| 				curG += ((RGBUShortDef*)scanbuf)[dw].Green;
 | |
| 				curB += ((RGBUShortDef*)scanbuf)[dw].Blue;
 | |
| 			}
 | |
| 		}
 | |
| 		curR /= len;
 | |
| 		curG /= len;
 | |
| 		curB /= len;
 | |
| 
 | |
| 		diffR = curR - lastR; lastR = curR;
 | |
| 		diffG = curG - lastG; lastG = curG;
 | |
| 		diffB = curB - lastB; lastB = curB;
 | |
| 		DBG( _DBG_INFO2, "%i/%i-AVE(R,G,B)= %lu(%ld), %lu(%ld), %lu(%ld)\n", 
 | |
| 		              i, stable_count, curR, diffR, curG, diffG, curB, diffB );
 | |
| 
 | |
| 		/* we consider the lamp to be stable,
 | |
| 		 * when the diffs are less than thresh for at least 3 loops
 | |
| 		 */
 | |
| 		if((diffR < thresh) && (diffG < thresh) && (diffB < thresh)) {
 | |
| 			if( stable_count > 3 )
 | |
| 				break;
 | |
| 			stable_count++;
 | |
| 		} else {
 | |
| 			stable_count = 0;
 | |
| 		}
 | |
| 
 | |
| 		/* no need to sleep in the first loop */
 | |
| 		if((i != 0) && (stable_count == 0))
 | |
| 			sleep( _AUTO_SLEEP );
 | |
| 	}
 | |
| 
 | |
| 	DBG( _DBG_INFO, "usb_AutoWarmup() done - %u loops\n", i+1 );
 | |
| 	DBG( _DBG_INFO, "* AVE(R,G,B)= %lu(%ld), %lu(%ld), %lu(%ld)\n", 
 | |
| 	                       curR, diffR, curG, diffG, curB, diffB );
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  */
 | |
| static int
 | |
| usb_DoIt( Plustek_Device *dev )
 | |
| {
 | |
| 	SANE_Bool skip_fine;
 | |
| 	ScanDef  *scan = &dev->scanning;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "Settings done, so start...\n" );
 | |
| 	if( !scan->skipCoarseCalib ) {
 | |
| 		DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" );
 | |
| 		if( !usb_AdjustGain(dev, 0)) {
 | |
| 			DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
 | |
| 			return _E_INTERNAL;
 | |
| 		}
 | |
| 		DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" );
 | |
| 		if( !usb_AdjustOffset(dev)) {
 | |
| 			DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" );
 | |
| 			return _E_INTERNAL;
 | |
| 		}
 | |
| 	} else {
 | |
| 		DBG( _DBG_INFO2, "Coarse Calibration skipped, using saved data\n" );
 | |
| 	}
 | |
| 
 | |
| 	skip_fine = SANE_FALSE;
 | |
| 	if( dev->adj.cacheCalData ) {
 | |
| 		skip_fine = usb_FineShadingFromFile(dev);
 | |
| 	}
 | |
| 
 | |
| 	if( !skip_fine ) {
 | |
| 		DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" );
 | |
| 		if( !usb_AdjustDarkShading(dev)) {
 | |
| 			DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
 | |
| 			return _E_INTERNAL;
 | |
| 		}
 | |
| 		DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" );
 | |
| 		if( !usb_AdjustWhiteShading(dev)) {
 | |
| 			DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" );
 | |
| 			return _E_INTERNAL;
 | |
| 		}
 | |
| 	} else {
 | |
| 		DBG( _DBG_INFO2, "###### FINE calibration skipped #######\n" );
 | |
| 
 | |
| 		m_ScanParam = scan->sParam;
 | |
| 		usb_GetPhyPixels( dev, &m_ScanParam );
 | |
| 
 | |
| 		usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels,
 | |
| 		                      m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
 | |
| 		usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels,
 | |
| 		                      m_ScanParam.bDataType == SCANDATATYPE_Color?1:0);
 | |
| 
 | |
| /*		dev->usbDev.a_bRegs[0x45] &= ~0x10;*/
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /** usb_DoCalibration
 | |
|  */
 | |
| static int
 | |
| usb_DoCalibration( Plustek_Device *dev )
 | |
| {
 | |
| 	int       result;
 | |
| 	ScanDef  *scanning = &dev->scanning;
 | |
| 	DCapsDef *scaps    = &dev->usbDev.Caps;
 | |
| 	HWDef    *hw       = &dev->usbDev.HwSetting;
 | |
| 	u_char   *regs     = dev->usbDev.a_bRegs;
 | |
| 	double    dRed, dGreen, dBlue;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "usb_DoCalibration()\n" );
 | |
| 
 | |
| 	if( SANE_TRUE == scanning->fCalibrated )
 | |
| 		return SANE_TRUE;
 | |
| 
 | |
| 	/* Go to shading position
 | |
|      */
 | |
| 	DBG( _DBG_INFO, "...goto shading position\n" );
 | |
| 
 | |
| 	/* HEINER: Currently not clear why Plustek didn't use the ShadingOriginY
 | |
| 	 *         for all modes
 | |
| 	 * It should be okay to remove this and reference to the ShadingOriginY
 | |
| 	 */	
 | |
| #if 0	
 | |
| 	if( scanning->sParam.bSource == SOURCE_Negative ) {
 | |
| 
 | |
| 		DBG( _DBG_INFO, "DataOrigin.x=%u, DataOrigin.y=%u\n",
 | |
| 		 dev->usbDev.pSource->DataOrigin.x, dev->usbDev.pSource->DataOrigin.y);
 | |
| 		if(!usb_ModuleMove( dev, MOVE_Forward,
 | |
| 		                                  (dev->usbDev.pSource->DataOrigin.y +
 | |
|   			                               dev->usbDev.pSource->Size.y / 2))) {
 | |
| 			return _E_LAMP_NOT_IN_POS;
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| #endif
 | |
| 		DBG( _DBG_INFO, "ShadingOriginY=%lu\n",
 | |
| 				(u_long)dev->usbDev.pSource->ShadingOriginY );
 | |
| 
 | |
| 		if((hw->motorModel == MODEL_HuaLien) && (scaps->OpticDpi.x==600)) {
 | |
| 			if (!usb_ModuleMove(dev, MOVE_ToShading,
 | |
| 			                    (u_long)dev->usbDev.pSource->ShadingOriginY)) {
 | |
| 				return _E_LAMP_NOT_IN_POS;
 | |
| 			}
 | |
| 		} else {
 | |
| 			if( !usb_ModuleMove(dev, MOVE_Forward,
 | |
| 			                    (u_long)dev->usbDev.pSource->ShadingOriginY)) {
 | |
| 				return _E_LAMP_NOT_IN_POS;
 | |
| 			}
 | |
| 		}
 | |
| /*	}*/
 | |
| 
 | |
| 	DBG( _DBG_INFO, "shading position reached\n" );
 | |
| 
 | |
| 	usb_SpeedTest( dev );
 | |
| 
 | |
| 	if( !usb_AutoWarmup( dev ))
 | |
| 		return SANE_FALSE;
 | |
| 
 | |
| 	usb_PrepareCalibration( dev );
 | |
| 
 | |
| 	/** this won't work for Plustek devices!!!
 | |
| 	 */
 | |
| #if 0
 | |
| 	if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ||
 | |
| 		!(SCANDEF_QualityScan & dev->scanning.dwFlag)) {
 | |
| #else
 | |
| 	if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
 | |
| #endif
 | |
| 
 | |
| 		DBG( _DBG_INFO, "--> BYPASS\n" );
 | |
| 		regs[0x38] = regs[0x39] = regs[0x3a] = 0;
 | |
| 		regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 		setAdjGain( dev->adj.rgain, ®s[0x3b] );
 | |
| 		setAdjGain( dev->adj.ggain, ®s[0x3c] );
 | |
| 		setAdjGain( dev->adj.bgain, ®s[0x3d] );
 | |
| 
 | |
| 		regs[0x45] |= 0x10;
 | |
| 		usb_SetMCLK( dev, &scanning->sParam );
 | |
| 
 | |
| 	    dumpregs( dev->fd, regs );
 | |
| 		DBG( _DBG_INFO, "<-- BYPASS\n" );
 | |
| 
 | |
| 	} else {
 | |
| 
 | |
| 		switch( scanning->sParam.bSource ) {
 | |
| 
 | |
| 			case SOURCE_Negative:
 | |
| 				DBG( _DBG_INFO, "NEGATIVE Shading\n" );
 | |
| 				m_dwIdealGain = IDEAL_GainNormal;
 | |
| 
 | |
| 				if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
 | |
| 					DBG( _DBG_INFO, "No Plustek model: %udpi\n",
 | |
| 						            scanning->sParam.PhyDpi.x );
 | |
| 					usb_SetMCLK( dev, &scanning->sParam );
 | |
| 				} else {
 | |
| 
 | |
| 					if( dev->usbDev.Caps.OpticDpi.x == 600 )
 | |
| 						dMCLK = 7;
 | |
| 					else
 | |
| 						dMCLK = 8;
 | |
| 				}
 | |
| 
 | |
| 				for(;;) {
 | |
| 					if( usb_AdjustGain( dev, 2)) {
 | |
| 						if( regs[0x3b] && regs[0x3c] && regs[0x3d]) {
 | |
| 							break;
 | |
| 						} else {
 | |
| 							regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 							dMCLK--;
 | |
| 						}
 | |
| 					} else {
 | |
| 						return _E_LAMP_NOT_STABLE;
 | |
| 					}
 | |
| 				}
 | |
| 				scanning->sParam.dMCLK = dMCLK;
 | |
| 				Gain_Reg.Red    = regs[0x3b];
 | |
| 				Gain_Reg.Green  = regs[0x3c];
 | |
| 				Gain_Reg.Blue   = regs[0x3d];
 | |
| 				Gain_NegHilight = Gain_Hilight;
 | |
| 			
 | |
| 				DBG( _DBG_INFO, "MCLK      = %.3f\n", dMCLK );
 | |
| 				DBG( _DBG_INFO, "GainRed   = %u\n", regs[0x3b] );
 | |
| 				DBG( _DBG_INFO, "GainGreen = %u\n", regs[0x3c] );
 | |
| 				DBG( _DBG_INFO, "GainBlue  = %u\n", regs[0x3d] );
 | |
| 
 | |
| #if 0
 | |
|     			if( !usb_ModuleMove( dev, MOVE_Backward,
 | |
|     								 dev->usbDev.pSource->DataOrigin.y +
 | |
|     								 dev->usbDev.pSource->Size.y / 2 -
 | |
|     								 dev->usbDev.pSource->ShadingOriginY)) {
 | |
|     				return _E_LAMP_NOT_IN_POS;
 | |
|     			}
 | |
| #endif
 | |
| 				regs[0x45] &= ~0x10;
 | |
| 
 | |
| 				regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 				if(!usb_AdjustGain( dev, 1 ))
 | |
| 					return _E_INTERNAL;
 | |
| 				
 | |
| 				regs[0x3b] = regs[0x3c] = regs[0x3d] = 1;
 | |
| 
 | |
| 				DBG( _DBG_INFO, "Settings done, so start...\n" );
 | |
| 				if( !usb_AdjustOffset(dev) || !usb_AdjustDarkShading(dev) ||
 | |
| 				                              !usb_AdjustWhiteShading(dev)) {
 | |
| 					return _E_INTERNAL;
 | |
| 				}
 | |
| 
 | |
| 				dRed    = 0.93 + 0.067 * Gain_Reg.Red;
 | |
| 				dGreen  = 0.93 + 0.067 * Gain_Reg.Green;
 | |
| 				dBlue   = 0.93 + 0.067 * Gain_Reg.Blue;
 | |
| 				dExpect = 2.85;
 | |
| 				if( dBlue >= dGreen && dBlue >= dRed )
 | |
| 					dMax = dBlue;
 | |
| 				else
 | |
| 					if( dGreen >= dRed && dGreen >= dBlue )
 | |
| 						dMax = dGreen;
 | |
| 					else
 | |
| 						dMax = dRed;
 | |
| 
 | |
| 				dMax = dExpect / dMax;
 | |
| 				dRed   *= dMax;
 | |
| 				dGreen *= dMax;
 | |
| 				dBlue  *= dMax;
 | |
| 
 | |
| 				if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 					usb_ResizeWhiteShading( dRed,  a_wWhiteShading,
 | |
| 					                                scanning->sParam.swGain[0]);
 | |
| 					usb_ResizeWhiteShading( dGreen, a_wWhiteShading +
 | |
| 					                                m_ScanParam.Size.dwPhyPixels,
 | |
| 					                                scanning->sParam.swGain[1]);
 | |
| 					usb_ResizeWhiteShading( dBlue,  a_wWhiteShading +
 | |
| 					                                m_ScanParam.Size.dwPhyPixels*2,
 | |
| 					                                scanning->sParam.swGain[2]);
 | |
| 				}
 | |
| 				usb_line_statistics( "White", a_wWhiteShading, 
 | |
| 				                     m_ScanParam.Size.dwPhyPixels, SANE_TRUE);
 | |
| 				break;
 | |
| 
 | |
| 			case SOURCE_ADF:
 | |
| 				DBG( _DBG_INFO, "ADF Shading\n" );
 | |
| 				m_dwIdealGain = IDEAL_GainPositive;
 | |
| 				if( scanning->sParam.bDataType == SCANDATATYPE_BW ) {
 | |
| 					if( scanning->sParam.PhyDpi.x <= 200 ) {
 | |
| 						scanning->sParam.dMCLK = 4.5;
 | |
| 						dMCLK = 4.0;
 | |
| 					} else if ( scanning->sParam.PhyDpi.x <= 300 ) {
 | |
| 						scanning->sParam.dMCLK = 4.0;
 | |
| 						dMCLK = 3.5;
 | |
| 					} else if( scanning->sParam.PhyDpi.x <= 400 ) {
 | |
| 						scanning->sParam.dMCLK = 5.0;
 | |
| 						dMCLK = 4.0;
 | |
| 					} else {
 | |
| 						scanning->sParam.dMCLK = 6.0;
 | |
| 						dMCLK = 4.0;
 | |
| 					}
 | |
| 				} else { /* Gray */
 | |
| 
 | |
| 					if( scanning->sParam.PhyDpi.x <= 400 ) {
 | |
| 						scanning->sParam.dMCLK = 6.0;
 | |
| 						dMCLK = 4.5;
 | |
| 					} else {
 | |
| 						scanning->sParam.dMCLK = 9.0;
 | |
| 						dMCLK = 7.0;
 | |
| 					}
 | |
| 				}
 | |
| 				dMCLK_ADF = dMCLK;
 | |
| 
 | |
| 				result = usb_DoIt( dev );
 | |
| 				if( result != 0 )
 | |
| 					return result;
 | |
| 				break;
 | |
| 
 | |
| 			case SOURCE_Transparency:
 | |
| 				DBG( _DBG_INFO, "TRANSPARENCY Shading\n" );
 | |
| 				m_dwIdealGain = IDEAL_GainPositive;
 | |
| 
 | |
| 				if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
 | |
| 					DBG( _DBG_INFO, "No Plustek model: %udpi\n",
 | |
| 						            scanning->sParam.PhyDpi.x );
 | |
| 					usb_SetMCLK( dev, &scanning->sParam );
 | |
| 
 | |
| 				} else {
 | |
| 					if( dev->usbDev.Caps.OpticDpi.x == 600 ) {
 | |
| 						scanning->sParam.dMCLK = 8;
 | |
| 						dMCLK = 4;
 | |
| 					} else {
 | |
| 						scanning->sParam.dMCLK = 8;
 | |
| 						dMCLK = 6;
 | |
| 					}
 | |
| 				}
 | |
| 				result = usb_DoIt( dev );
 | |
| 				if( result != 0 )
 | |
| 					return result;
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				if( !_IS_PLUSTEKMOTOR(hw->motorModel)) {
 | |
|     				DBG( _DBG_INFO, "No Plustek model: %udpi\n",
 | |
|     												scanning->sParam.PhyDpi.x );
 | |
|     				usb_SetMCLK( dev, &scanning->sParam );
 | |
| 
 | |
|     			} else 	if( dev->usbDev.Caps.OpticDpi.x == 600 ) {
 | |
| 
 | |
|     				DBG( _DBG_INFO, "Default Shading (600dpi)\n" );
 | |
| 
 | |
|     				if( dev->usbDev.Caps.bCCD == kSONY548 )	{
 | |
| 
 | |
|     					DBG( _DBG_INFO, "CCD - SONY548\n" );
 | |
|     					if( scanning->sParam.PhyDpi.x <= 75 ) {
 | |
|     						if( scanning->sParam.bDataType  == SCANDATATYPE_Color )
 | |
|     							scanning->sParam.dMCLK = dMCLK = 2.5;
 | |
|     						else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
 | |
|     							scanning->sParam.dMCLK = dMCLK = 7.0;
 | |
|     						else
 | |
|     							scanning->sParam.dMCLK = dMCLK = 7.0;
 | |
| 
 | |
|     					} else if( scanning->sParam.PhyDpi.x <= 300 ) {
 | |
|     						if( scanning->sParam.bDataType  == SCANDATATYPE_Color )
 | |
|     							scanning->sParam.dMCLK = dMCLK = 3.0;
 | |
|     						else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
 | |
|     							scanning->sParam.dMCLK = dMCLK = 6.0;
 | |
|     						else  {
 | |
|     							if( scanning->sParam.PhyDpi.x <= 100 )
 | |
|     								scanning->sParam.dMCLK = dMCLK = 6.0;
 | |
|     							else if( scanning->sParam.PhyDpi.x <= 200 )
 | |
|     								scanning->sParam.dMCLK = dMCLK = 5.0;
 | |
|     							else
 | |
|     								scanning->sParam.dMCLK = dMCLK = 4.5;
 | |
|     						}
 | |
|     					} else if( scanning->sParam.PhyDpi.x <= 400 ) {
 | |
|     						if( scanning->sParam.bDataType  == SCANDATATYPE_Color )
 | |
|     							scanning->sParam.dMCLK = dMCLK = 4.0;
 | |
|     						else if( scanning->sParam.bDataType == SCANDATATYPE_Gray )
 | |
|     							scanning->sParam.dMCLK = dMCLK = 6.0;
 | |
|     						else
 | |
|     							scanning->sParam.dMCLK = dMCLK = 4.0;
 | |
|     					} else {
 | |
|     						if(scanning->sParam.bDataType  == SCANDATATYPE_Color)
 | |
|     							scanning->sParam.dMCLK = dMCLK = 6.0;
 | |
|     						else if(scanning->sParam.bDataType == SCANDATATYPE_Gray)
 | |
|     							scanning->sParam.dMCLK = dMCLK = 7.0;
 | |
|     						else
 | |
|     							scanning->sParam.dMCLK = dMCLK = 6.0;
 | |
|     					}
 | |
| 
 | |
|     				} else if( dev->usbDev.Caps.bPCB == 0x02 ) {
 | |
|     					DBG( _DBG_INFO, "PCB - 0x02\n" );
 | |
|     					if( scanning->sParam.PhyDpi.x > 300 )
 | |
|     						scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 16);
 | |
|     					else if( scanning->sParam.PhyDpi.x > 150 )
 | |
|     						scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?4.5: 13.5);
 | |
|     					else
 | |
|     						scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 8);
 | |
| 
 | |
|     				}  else if( dev->usbDev.Caps.bButtons ) { /* with lens Shading piece (with gobo) */
 | |
| 
 | |
|     					DBG( _DBG_INFO, "CAPS - Buttons\n" );
 | |
|     					scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
 | |
|     					if( dev->usbDev.HwSetting.motorModel == MODEL_KaoHsiung )	{
 | |
|     						if( dev->usbDev.Caps.bCCD == kNEC3799 ) {
 | |
|     							if( scanning->sParam.PhyDpi.x > 300 )
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 13);
 | |
|     							else if(scanning->sParam.PhyDpi.x > 150)
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?4.5:13.5);
 | |
|     							else
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
 | |
|     						} else {
 | |
|     							scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
 | |
|     						}
 | |
|     					} else { /* motorModel == MODEL_Hualien */
 | |
|     						/*  IMPORTANT !!!!
 | |
|     						 * for Hualien 600 dpi scanner big noise
 | |
|                              */
 | |
|     						hw->wLineEnd = 5384;
 | |
|     						if(scanning->sParam.bDataType  == SCANDATATYPE_Color &&
 | |
|     							((scanning->sParam.bBitDepth == 8 && 
 | |
| 								 (scanning->sParam.PhyDpi.x == 200 ||scanning->sParam.PhyDpi.x == 300))))
 | |
|     							hw->wLineEnd = 7000;
 | |
|     						regs[0x20] = _HIBYTE(hw->wLineEnd);
 | |
|     						regs[0x21] = _LOBYTE(hw->wLineEnd);
 | |
| 
 | |
|     						if( scanning->sParam.PhyDpi.x > 300 ) {
 | |
|     							if (scanning->sParam.bBitDepth > 8)
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType  == SCANDATATYPE_Color)? 5: 13);
 | |
|     							else
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 6: 13);
 | |
|     						} else {
 | |
|     							if( scanning->sParam.bBitDepth > 8 )
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 5: 13);
 | |
|     							else
 | |
|     								scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)?3: 6);
 | |
|     						}
 | |
|     					}
 | |
|     				} else  { /* without lens Shading piece (without gobo) - Model U12 only */
 | |
|     					DBG( _DBG_INFO, "Default trunc (U12)\n" );
 | |
|     					if( scanning->sParam.PhyDpi.x > 300 )
 | |
|     						scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 3: 9);
 | |
|     					else
 | |
|     						scanning->sParam.dMCLK = dMCLK = ((scanning->sParam.bDataType == SCANDATATYPE_Color)? 2: 6);
 | |
|     				}
 | |
|     			} else {	/* Device.Caps.OpticDpi.x == 1200 */
 | |
| 
 | |
|     				DBG( _DBG_INFO, "Default Shading (1200dpi)\n" );
 | |
|     				if( scanning->sParam.bDataType != SCANDATATYPE_Color ) {
 | |
|     					if( scanning->sParam.PhyDpi.x > 300 )
 | |
|     						scanning->sParam.dMCLK = dMCLK = 6.0;
 | |
|     					else {
 | |
|     						scanning->sParam.dMCLK = dMCLK = 5.0;
 | |
|     						regs[0x0a] = 1;
 | |
|     					}
 | |
|     				} else {
 | |
|     					if( scanning->sParam.PhyDpi.x <= 300)
 | |
|     						scanning->sParam.dMCLK = dMCLK = 2.0;
 | |
|     					else if( scanning->sParam.PhyDpi.x <= 800 )
 | |
|     						scanning->sParam.dMCLK = dMCLK = 4.0;
 | |
|     					else
 | |
|     						scanning->sParam.dMCLK = dMCLK = 5.5;
 | |
|     				}
 | |
|     			}
 | |
| 
 | |
| 				if (m_ScanParam.bSource == SOURCE_ADF)
 | |
| 					m_dwIdealGain = IDEAL_GainPositive;
 | |
| 				else
 | |
| 					m_dwIdealGain = IDEAL_GainNormal;
 | |
| 
 | |
| 				result = usb_DoIt( dev );
 | |
| 				if( result != 0 )
 | |
| 					return result;
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* home the sensor after calibration */
 | |
| 	if( _IS_PLUSTEKMOTOR(hw->motorModel)) {
 | |
| 		if( hw->motorModel != MODEL_Tokyo600 ) {
 | |
| 			usb_ModuleMove  ( dev, MOVE_Forward, hw->wMotorDpi / 5 );
 | |
| 			usb_ModuleToHome( dev, SANE_TRUE );
 | |
| 		}
 | |
| 	} else {
 | |
| 		usb_ModuleMove( dev, MOVE_Forward, 10 );
 | |
| 		usleep( 1500 );
 | |
| 		usb_ModuleToHome( dev, SANE_TRUE );
 | |
| 	}
 | |
| 
 | |
| 	if( scanning->sParam.bSource == SOURCE_ADF ) {
 | |
| 
 | |
| 		if( scaps->bCCD == kNEC3778 )
 | |
| 			usb_ModuleMove( dev, MOVE_Forward, 1000 );
 | |
| 
 | |
| 		else /* if( scaps->bCCD == kNEC3799) */
 | |
| 			usb_ModuleMove( dev, MOVE_Forward, 3 * 300 + 38 );
 | |
| 
 | |
| 		usb_MotorOn( dev, SANE_FALSE );
 | |
| 	}
 | |
| 
 | |
| 	scanning->fCalibrated = SANE_TRUE;
 | |
| 	DBG( _DBG_INFO, "Calibration done\n" );
 | |
| 	DBG( _DBG_INFO, "-----------------------\n" );
 | |
| 	DBG( _DBG_INFO, "Static Gain:\n" );
 | |
| 	DBG( _DBG_INFO, "REG[0x3b] = %u\n", regs[0x3b] );
 | |
| 	DBG( _DBG_INFO, "REG[0x3c] = %u\n", regs[0x3c] );
 | |
| 	DBG( _DBG_INFO, "REG[0x3d] = %u\n", regs[0x3d] );
 | |
| 	DBG( _DBG_INFO, "Static Offset:\n" );
 | |
| 	DBG( _DBG_INFO, "REG[0x38] = %i\n", regs[0x38] );
 | |
| 	DBG( _DBG_INFO, "REG[0x39] = %i\n", regs[0x39] );
 | |
| 	DBG( _DBG_INFO, "REG[0x3a] = %i\n", regs[0x3a] );
 | |
| 	DBG( _DBG_INFO, "MCLK      = %.2f\n", scanning->sParam.dMCLK );
 | |
| 	DBG( _DBG_INFO, "-----------------------\n" );
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /* on different sensor orders, we need to adjust the shading buffer
 | |
|  * pointer, otherwise we correct the wrong channels
 | |
|  */
 | |
| static void
 | |
| get_ptrs(Plustek_Device *dev, u_short *buf, u_long offs,
 | |
|          u_short **r, u_short **g, u_short **b)
 | |
| {
 | |
| 	ScanDef  *scan  = &dev->scanning;
 | |
| 	DCapsDef *scaps = &dev->usbDev.Caps;
 | |
| 	u_char so = scaps->bSensorOrder;
 | |
| 
 | |
| 	if (_WAF_RESET_SO_TO_RGB & scaps->workaroundFlag) {
 | |
| 		if (scaps->bPCB != 0) {
 | |
| 			if (scan->sParam.PhyDpi.x > scaps->bPCB)
 | |
| 				so = SENSORORDER_rgb;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch( so ) {
 | |
| 		case SENSORORDER_gbr:
 | |
| 			*g = buf;
 | |
| 			*b = buf + offs;
 | |
| 			*r = buf + offs * 2;
 | |
| 			break;
 | |
| 
 | |
| 		case SENSORORDER_bgr:
 | |
| 			*b = buf;
 | |
| 			*g = buf + offs;
 | |
| 			*r = buf + offs * 2;
 | |
| 			break;
 | |
| 
 | |
| 		case SENSORORDER_rgb:
 | |
| 		default:
 | |
| 			*r = buf;
 | |
| 			*g = buf + offs;
 | |
| 			*b = buf + offs * 2;
 | |
| 			break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** usb_DownloadShadingData
 | |
|  * according to the job id, different registers or DRAM areas are set
 | |
|  * in preparation for calibration or scanning
 | |
|  */
 | |
| static SANE_Bool
 | |
| usb_DownloadShadingData( Plustek_Device *dev, u_char what )
 | |
| {
 | |
| 	u_char    channel;
 | |
| 	u_short   *r, *g, *b;
 | |
| 	DCapsDef  *scaps = &dev->usbDev.Caps;
 | |
| 	ScanDef   *scan  = &dev->scanning;
 | |
| 	HWDef     *hw    = &dev->usbDev.HwSetting;
 | |
| 	ScanParam *param = &dev->scanning.sParam;
 | |
| 	u_char    *regs  = dev->usbDev.a_bRegs;
 | |
| 
 | |
| 	DBG( _DBG_INFO, "usb_DownloadShadingData(%u)\n", what );
 | |
| 
 | |
| 	channel = CHANNEL_green;
 | |
| 	if( usb_IsCISDevice(dev))
 | |
| 		channel = CHANNEL_blue;
 | |
| 
 | |
| 	switch( what ) {
 | |
| 
 | |
| 		case PARAM_WhiteShading:
 | |
| 			if( m_ScanParam.bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 				usb_SetDarkShading( dev, CHANNEL_red, a_wDarkShading,
 | |
| 				                    (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 				usb_SetDarkShading( dev, CHANNEL_green, a_wDarkShading +
 | |
| 				                    m_ScanParam.Size.dwPhyPixels,
 | |
| 				                    (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 				usb_SetDarkShading( dev, CHANNEL_blue, a_wDarkShading +
 | |
| 				                    m_ScanParam.Size.dwPhyPixels * 2,
 | |
| 				                    (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 			} else {
 | |
| 				usb_SetDarkShading( dev, channel, a_wDarkShading +
 | |
| 				                    m_ScanParam.Size.dwPhyPixels,
 | |
| 				                    (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 			}
 | |
| 			regs[0x40] = 0x40;
 | |
| 			regs[0x41] = 0x00;
 | |
| 
 | |
| 			/* set RAM configuration AND
 | |
| 			 * Gain = Multiplier Coefficient/16384
 | |
| 			 * CFG Register 0x40/0x41 for Multiplier Coefficient Source
 | |
| 			 * External DRAM for Offset Coefficient Source
 | |
| 			 */
 | |
| 			regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x64: 0x24);
 | |
| 			_UIO(sanei_lm983x_write( dev->fd, 0x40,
 | |
| 			                        ®s[0x40], 0x42-0x40+1, SANE_TRUE ));
 | |
| 			break;
 | |
| 
 | |
| 		case PARAM_Scan:
 | |
| 			{
 | |
| #if 0
 | |
| 				if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ||
 | |
| 					!(SCANDEF_QualityScan & dev->scanning.dwFlag)) {
 | |
| #else
 | |
| 				if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
 | |
| #endif
 | |
| 					DBG( _DBG_INFO, "--> BYPASS\n" );
 | |
| 					/* set RAM configuration AND
 | |
| 					 * Bypass Multiplier
 | |
| 					 * CFG Register 0x40/0x41 for Multiplier Coefficient Source
 | |
| 					 * CFG Register 0x3e/0x3f for Offset Coefficient Source
 | |
| 					 */
 | |
| 					regs[0x03] = 0;
 | |
| 					regs[0x40] = 0x40;
 | |
| 					regs[0x41] = 0x00;
 | |
| 					regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x61:0x21);
 | |
| 					if( !usbio_WriteReg( dev->fd, 0x03, regs[0x03]))
 | |
| 						return SANE_FALSE;
 | |
| 
 | |
| 					_UIO(sanei_lm983x_write( dev->fd, 0x40,
 | |
| 					                        ®s[0x40], 3, SANE_TRUE));
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				if( _LM9831 != hw->chip )
 | |
| 					m_dwPixels = m_ScanParam.Size.dwPhyPixels;
 | |
| 
 | |
| 				if( scaps->workaroundFlag & _WAF_SKIP_FINE ) {
 | |
| 					DBG( _DBG_INFO, "Skipping fine calibration\n" );
 | |
| 					regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x60: 0x20);
 | |
| 					if (scan->skipCoarseCalib) {
 | |
| 
 | |
| 						DBG( _DBG_INFO, "...cleaning shading buffer\n" );
 | |
| 						memset( a_wWhiteShading, 0, _SHADING_BUF );
 | |
| 						memset( a_wDarkShading,  0, _SHADING_BUF );
 | |
| 
 | |
| 						regs[0x40] = 0x3f;
 | |
| 						regs[0x41] = 0xff;
 | |
| 
 | |
| 						_UIO(sanei_lm983x_write( dev->fd, 0x40,
 | |
| 						                        ®s[0x40], 3, SANE_TRUE));
 | |
| 					} else {
 | |
| 						if( !usbio_WriteReg( dev->fd, 0x42, regs[0x42]))
 | |
| 							return SANE_FALSE;
 | |
| 					}
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				DBG( _DBG_INFO, "Downloading %lu pixels\n", m_dwPixels );
 | |
| 				/* Download the dark & white shadings to LM983x */
 | |
| 				if( param->bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 					get_ptrs(dev, a_wDarkShading, m_dwPixels, &r, &g, &b);
 | |
| 
 | |
| 					usb_SetDarkShading( dev, CHANNEL_red, r,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 					usb_SetDarkShading( dev, CHANNEL_green, g,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 					usb_SetDarkShading( dev, CHANNEL_blue, b,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 				} else {
 | |
| 					usb_SetDarkShading( dev, channel,
 | |
| 					                    a_wDarkShading + m_dwPixels,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 				}
 | |
| 				if( param->bDataType == SCANDATATYPE_Color ) {
 | |
| 
 | |
| 					get_ptrs(dev, a_wWhiteShading, 
 | |
| 					         m_ScanParam.Size.dwPhyPixels, &r, &g, &b);
 | |
| 
 | |
| 					usb_SetWhiteShading( dev, CHANNEL_red, r,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 					usb_SetWhiteShading( dev, CHANNEL_green, g,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 					usb_SetWhiteShading( dev, CHANNEL_blue, b,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 				} else {
 | |
| 					usb_SetWhiteShading( dev, channel, a_wWhiteShading,
 | |
| 					                (u_short)m_ScanParam.Size.dwPhyPixels * 2);
 | |
| 				}
 | |
| 
 | |
| 				/* set RAM configuration AND
 | |
| 				 * Gain = Multiplier Coefficient/16384
 | |
| 				 * External DRAM for Multiplier Coefficient Source
 | |
| 				 * External DRAM for Offset Coefficient Source
 | |
| 				 */
 | |
| 				regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x66: 0x26);
 | |
| 
 | |
| 				if( scaps->workaroundFlag & _WAF_SKIP_WHITEFINE ) {
 | |
| 					DBG( _DBG_INFO,"Skipping fine white calibration result\n");
 | |
| 					regs[0x42] = (u_char)(( hw->wDRAMSize > 512)? 0x64: 0x24);
 | |
| 				}
 | |
| 
 | |
| 				if( !usbio_WriteReg( dev->fd, 0x42, regs[0x42]))
 | |
| 					return SANE_FALSE;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			/* for coarse calibration and "black fine" */
 | |
| 			regs[0x3e] = 0;
 | |
| 			regs[0x3f] = 0;
 | |
| 			regs[0x40] = 0x40;
 | |
| 			regs[0x41] = 0x00;
 | |
| 
 | |
| 			/* set RAM configuration AND
 | |
| 			 * GAIN = Multiplier Coefficient/16384
 | |
| 			 * CFG Register 0x40/0x41 for Multiplier Coefficient Source
 | |
| 			 * CFG Register 0x3e/0x3f for Offset Coefficient Source
 | |
| 			 */
 | |
| 			regs[0x42] = (u_char)((hw->wDRAMSize > 512)? 0x60: 0x20);
 | |
| 			_UIO(sanei_lm983x_write( dev->fd, 0x3e, ®s[0x3e],
 | |
| 			                         0x42 - 0x3e + 1, SANE_TRUE ));
 | |
| 			break;
 | |
| 	}
 | |
| 	return SANE_TRUE;
 | |
| }
 | |
| 
 | |
| /* END PLUSTEK-USBSHADING.C .................................................*/
 |