1141cc406Sopenharmony_ci/*............................................................................. 2141cc406Sopenharmony_ci * Project : SANE library for Plustek flatbed scanners; canoscan calibration 3141cc406Sopenharmony_ci *............................................................................. 4141cc406Sopenharmony_ci */ 5141cc406Sopenharmony_ci 6141cc406Sopenharmony_ci/** @file plustek-usbcal.c 7141cc406Sopenharmony_ci * @brief Calibration routines for CanoScan CIS devices. 8141cc406Sopenharmony_ci * 9141cc406Sopenharmony_ci * Based on sources acquired from Plustek Inc.<br> 10141cc406Sopenharmony_ci * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de><br> 11141cc406Sopenharmony_ci * Large parts Copyright (C) 2003 Christopher Montgomery <monty@xiph.org> 12141cc406Sopenharmony_ci * 13141cc406Sopenharmony_ci * Montys' comment: 14141cc406Sopenharmony_ci * The basic premise: The stock Plustek-usbshading.c in the plustek 15141cc406Sopenharmony_ci * driver is effectively nonfunctional for Canon CanoScan scanners. 16141cc406Sopenharmony_ci * These scanners rely heavily on all calibration steps, especially 17141cc406Sopenharmony_ci * fine white, to produce acceptable scan results. However, to make 18141cc406Sopenharmony_ci * autocalibration work and make it work well involves some 19141cc406Sopenharmony_ci * substantial mucking aobut in code that supports thirty other 20141cc406Sopenharmony_ci * scanners with widely varying characteristics... none of which I own 21141cc406Sopenharmony_ci * or can test. 22141cc406Sopenharmony_ci * 23141cc406Sopenharmony_ci * Therefore, I'm splitting out a few calibration functions I need 24141cc406Sopenharmony_ci * to modify for the CanoScan which allows me to simplify things 25141cc406Sopenharmony_ci * greatly for the CanoScan without worrying about breaking other 26141cc406Sopenharmony_ci * scanners, as well as reuse the vast majority of the Plustek 27141cc406Sopenharmony_ci * driver infrastructure without forking. 28141cc406Sopenharmony_ci * 29141cc406Sopenharmony_ci * History: 30141cc406Sopenharmony_ci * - 0.45m - birth of the file; tested extensively with the LiDE 20 31141cc406Sopenharmony_ci * - 0.46 - renamed to plustek-usbcal.c 32141cc406Sopenharmony_ci * - fixed problems with LiDE30, works now with 650, 1220, 670, 1240 33141cc406Sopenharmony_ci * - cleanup 34141cc406Sopenharmony_ci * - added CCD calibration capability 35141cc406Sopenharmony_ci * - added the usage of the swGain and swOffset values, to allow 36141cc406Sopenharmony_ci * tweaking the calibration results on a sensor base 37141cc406Sopenharmony_ci * - 0.47 - moved usb_HostSwap() to plustek_usbhw.c 38141cc406Sopenharmony_ci * - fixed problem in cano_AdjustLightsource(), so that it won't 39141cc406Sopenharmony_ci * stop too early. 40141cc406Sopenharmony_ci * - 0.48 - cleanup 41141cc406Sopenharmony_ci * - 0.49 - a_bRegs is now part of the device structure 42141cc406Sopenharmony_ci * - fixed lampsetting in cano_AdjustLightsource() 43141cc406Sopenharmony_ci * - 0.50 - tried to use the settings from SANE-1.0.13 44141cc406Sopenharmony_ci * - added _TWEAK_GAIN to allow increasing GAIN during 45141cc406Sopenharmony_ci * lamp coarse calibration 46141cc406Sopenharmony_ci * - added also speedtest 47141cc406Sopenharmony_ci * - fixed segfault in fine calibration 48141cc406Sopenharmony_ci * - 0.51 - added fine calibration cache 49141cc406Sopenharmony_ci * - usb_SwitchLamp() now really switches off the sensor 50141cc406Sopenharmony_ci * - 0.52 - fixed setting for frontend values (gain/offset) 51141cc406Sopenharmony_ci * - added 0 pixel detection for offset calculation 52141cc406Sopenharmony_ci * 53141cc406Sopenharmony_ci * This file is part of the SANE package. 54141cc406Sopenharmony_ci * 55141cc406Sopenharmony_ci * This program is free software; you can redistribute it and/or 56141cc406Sopenharmony_ci * modify it under the terms of the GNU General Public License as 57141cc406Sopenharmony_ci * published by the Free Software Foundation; either version 2 of the 58141cc406Sopenharmony_ci * License, or (at your option) any later version. 59141cc406Sopenharmony_ci * 60141cc406Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 61141cc406Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 62141cc406Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 63141cc406Sopenharmony_ci * General Public License for more details. 64141cc406Sopenharmony_ci * 65141cc406Sopenharmony_ci * You should have received a copy of the GNU General Public License 66141cc406Sopenharmony_ci * along with this program. If not, see <https://www.gnu.org/licenses/>. 67141cc406Sopenharmony_ci * 68141cc406Sopenharmony_ci * As a special exception, the authors of SANE give permission for 69141cc406Sopenharmony_ci * additional uses of the libraries contained in this release of SANE. 70141cc406Sopenharmony_ci * 71141cc406Sopenharmony_ci * The exception is that, if you link a SANE library with other files 72141cc406Sopenharmony_ci * to produce an executable, this does not by itself cause the 73141cc406Sopenharmony_ci * resulting executable to be covered by the GNU General Public 74141cc406Sopenharmony_ci * License. Your use of that executable is in no way restricted on 75141cc406Sopenharmony_ci * account of linking the SANE library code into it. 76141cc406Sopenharmony_ci * 77141cc406Sopenharmony_ci * This exception does not, however, invalidate any other reasons why 78141cc406Sopenharmony_ci * the executable file might be covered by the GNU General Public 79141cc406Sopenharmony_ci * License. 80141cc406Sopenharmony_ci * 81141cc406Sopenharmony_ci * If you submit changes to SANE to the maintainers to be included in 82141cc406Sopenharmony_ci * a subsequent release, you agree by submitting the changes that 83141cc406Sopenharmony_ci * those changes may be distributed with this exception intact. 84141cc406Sopenharmony_ci * 85141cc406Sopenharmony_ci * If you write modifications of your own for SANE, it is your choice 86141cc406Sopenharmony_ci * whether to permit this exception to apply to your modifications. 87141cc406Sopenharmony_ci * If you do not wish that, delete this exception notice. 88141cc406Sopenharmony_ci * <hr> 89141cc406Sopenharmony_ci */ 90141cc406Sopenharmony_ci 91141cc406Sopenharmony_ci/* un-/comment the following to en-/disable lamp coarse calibration to tweak 92141cc406Sopenharmony_ci * the initial AFE gain settings 93141cc406Sopenharmony_ci */ 94141cc406Sopenharmony_ci#define _TWEAK_GAIN 1 95141cc406Sopenharmony_ci 96141cc406Sopenharmony_ci/* set the threshold for 0 pixels (in percent if pixels per line) */ 97141cc406Sopenharmony_ci#define _DARK_TGT_THRESH 1 98141cc406Sopenharmony_ci 99141cc406Sopenharmony_ci/** 0 for not ready, 1 pos white lamp on, 2 lamp off */ 100141cc406Sopenharmony_cistatic int strip_state = 0; 101141cc406Sopenharmony_ci 102141cc406Sopenharmony_ci/** depending on the strip state, the sensor is moved to the shading position 103141cc406Sopenharmony_ci * and the lamp is switched on 104141cc406Sopenharmony_ci */ 105141cc406Sopenharmony_cistatic int 106141cc406Sopenharmony_cicano_PrepareToReadWhiteCal( Plustek_Device *dev, SANE_Bool mv2shading_pos ) 107141cc406Sopenharmony_ci{ 108141cc406Sopenharmony_ci SANE_Bool goto_shading_pos = SANE_TRUE; 109141cc406Sopenharmony_ci HWDef *hw = &dev->usbDev.HwSetting; 110141cc406Sopenharmony_ci 111141cc406Sopenharmony_ci switch (strip_state) { 112141cc406Sopenharmony_ci case 0: 113141cc406Sopenharmony_ci if( !usb_IsSheetFedDevice(dev)) { 114141cc406Sopenharmony_ci if(!usb_ModuleToHome( dev, SANE_TRUE )) { 115141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" ); 116141cc406Sopenharmony_ci return _E_LAMP_NOT_IN_POS; 117141cc406Sopenharmony_ci } 118141cc406Sopenharmony_ci } else { 119141cc406Sopenharmony_ci goto_shading_pos = mv2shading_pos; 120141cc406Sopenharmony_ci } 121141cc406Sopenharmony_ci 122141cc406Sopenharmony_ci if( goto_shading_pos ) { 123141cc406Sopenharmony_ci if( !usb_ModuleMove(dev, MOVE_Forward, 124141cc406Sopenharmony_ci (u_long)dev->usbDev.pSource->ShadingOriginY)) { 125141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" ); 126141cc406Sopenharmony_ci return _E_LAMP_NOT_IN_POS; 127141cc406Sopenharmony_ci } 128141cc406Sopenharmony_ci } 129141cc406Sopenharmony_ci break; 130141cc406Sopenharmony_ci case 2: 131141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29; 132141cc406Sopenharmony_ci usb_switchLamp( dev, SANE_TRUE ); 133141cc406Sopenharmony_ci if( !usbio_WriteReg( dev->fd, 0x29, dev->usbDev.a_bRegs[0x29])) { 134141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_PrepareToReadWhiteCal() failed\n" ); 135141cc406Sopenharmony_ci return _E_LAMP_NOT_IN_POS; 136141cc406Sopenharmony_ci } 137141cc406Sopenharmony_ci break; 138141cc406Sopenharmony_ci } 139141cc406Sopenharmony_ci 140141cc406Sopenharmony_ci strip_state = 1; 141141cc406Sopenharmony_ci return 0; 142141cc406Sopenharmony_ci} 143141cc406Sopenharmony_ci 144141cc406Sopenharmony_ci/** also here, depending on the strip state, the sensor will be moved to 145141cc406Sopenharmony_ci * the shading position and the lamp will be switched off 146141cc406Sopenharmony_ci */ 147141cc406Sopenharmony_cistatic int 148141cc406Sopenharmony_cicano_PrepareToReadBlackCal( Plustek_Device *dev ) 149141cc406Sopenharmony_ci{ 150141cc406Sopenharmony_ci if( strip_state == 0 ) 151141cc406Sopenharmony_ci if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE)) 152141cc406Sopenharmony_ci return SANE_FALSE; 153141cc406Sopenharmony_ci 154141cc406Sopenharmony_ci if( strip_state != 2 ) { 155141cc406Sopenharmony_ci /* 156141cc406Sopenharmony_ci * if we have a dark shading strip, there's no need to switch 157141cc406Sopenharmony_ci * the lamp off, leave in on a go to that strip 158141cc406Sopenharmony_ci */ 159141cc406Sopenharmony_ci if( dev->usbDev.pSource->DarkShadOrgY >= 0 ) { 160141cc406Sopenharmony_ci 161141cc406Sopenharmony_ci if( !usb_IsSheetFedDevice(dev)) 162141cc406Sopenharmony_ci usb_ModuleToHome( dev, SANE_TRUE ); 163141cc406Sopenharmony_ci usb_ModuleMove ( dev, MOVE_Forward, 164141cc406Sopenharmony_ci (u_long)dev->usbDev.pSource->DarkShadOrgY ); 165141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x45] &= ~0x10; 166141cc406Sopenharmony_ci strip_state = 0; 167141cc406Sopenharmony_ci 168141cc406Sopenharmony_ci } else { 169141cc406Sopenharmony_ci /* switch lamp off to read dark data... */ 170141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x29] = 0; 171141cc406Sopenharmony_ci usb_switchLamp( dev, SANE_FALSE ); 172141cc406Sopenharmony_ci strip_state = 2; 173141cc406Sopenharmony_ci } 174141cc406Sopenharmony_ci } 175141cc406Sopenharmony_ci return 0; 176141cc406Sopenharmony_ci} 177141cc406Sopenharmony_ci 178141cc406Sopenharmony_ci/** according to the strip-state we switch the lamp on 179141cc406Sopenharmony_ci */ 180141cc406Sopenharmony_cistatic int 181141cc406Sopenharmony_cicano_LampOnAfterCalibration( Plustek_Device *dev ) 182141cc406Sopenharmony_ci{ 183141cc406Sopenharmony_ci HWDef *hw = &dev->usbDev.HwSetting; 184141cc406Sopenharmony_ci 185141cc406Sopenharmony_ci switch (strip_state) { 186141cc406Sopenharmony_ci case 2: 187141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29; 188141cc406Sopenharmony_ci usb_switchLamp( dev, SANE_TRUE ); 189141cc406Sopenharmony_ci if( !usbio_WriteReg( dev->fd, 0x29, dev->usbDev.a_bRegs[0x29])) { 190141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_LampOnAfterCalibration() failed\n" ); 191141cc406Sopenharmony_ci return _E_LAMP_NOT_IN_POS; 192141cc406Sopenharmony_ci } 193141cc406Sopenharmony_ci strip_state = 1; 194141cc406Sopenharmony_ci break; 195141cc406Sopenharmony_ci } 196141cc406Sopenharmony_ci return 0; 197141cc406Sopenharmony_ci} 198141cc406Sopenharmony_ci 199141cc406Sopenharmony_ci/** function to adjust the CIS lamp-off setting for a given channel. 200141cc406Sopenharmony_ci * @param min - pointer to the min OFF point for the CIS-channel 201141cc406Sopenharmony_ci * @param max - pointer to the max OFF point for the CIS-channel 202141cc406Sopenharmony_ci * @param off - pointer to the current OFF point of the CIS-channel 203141cc406Sopenharmony_ci * @param val - current value to check 204141cc406Sopenharmony_ci * @return returns 0 if the value is fine, 1, if we need to adjust 205141cc406Sopenharmony_ci */ 206141cc406Sopenharmony_cistatic int 207141cc406Sopenharmony_cicano_adjLampSetting( u_short *min, u_short *max, u_short *off, u_short val ) 208141cc406Sopenharmony_ci{ 209141cc406Sopenharmony_ci u_long newoff = *off; 210141cc406Sopenharmony_ci 211141cc406Sopenharmony_ci /* perfect value, no need to adjust 212141cc406Sopenharmony_ci * val [53440..61440] is perfect 213141cc406Sopenharmony_ci */ 214141cc406Sopenharmony_ci if((val < (IDEAL_GainNormal)) && (val > (IDEAL_GainNormal-8000))) 215141cc406Sopenharmony_ci return 0; 216141cc406Sopenharmony_ci 217141cc406Sopenharmony_ci if(val >= (IDEAL_GainNormal-4000)) { 218141cc406Sopenharmony_ci DBG(_DBG_INFO2, "* TOO BRIGHT --> reduce\n" ); 219141cc406Sopenharmony_ci *max = newoff; 220141cc406Sopenharmony_ci *off = ((newoff + *min)>>1); 221141cc406Sopenharmony_ci 222141cc406Sopenharmony_ci } else { 223141cc406Sopenharmony_ci 224141cc406Sopenharmony_ci u_short bisect = (newoff + *max)>>1; 225141cc406Sopenharmony_ci u_short twice = newoff*2; 226141cc406Sopenharmony_ci 227141cc406Sopenharmony_ci DBG(_DBG_INFO2, "* TOO DARK --> up\n" ); 228141cc406Sopenharmony_ci *min = newoff; 229141cc406Sopenharmony_ci *off = twice<bisect?twice:bisect; 230141cc406Sopenharmony_ci 231141cc406Sopenharmony_ci /* as we have already set the maximum value, there's no need 232141cc406Sopenharmony_ci * for this channel to recalibrate. 233141cc406Sopenharmony_ci */ 234141cc406Sopenharmony_ci if( *off > 0x3FFF ) { 235141cc406Sopenharmony_ci DBG( _DBG_INFO, "* lamp off limited (0x%04x --> 0x3FFF)\n", *off); 236141cc406Sopenharmony_ci *off = 0x3FFF; 237141cc406Sopenharmony_ci return 10; 238141cc406Sopenharmony_ci } 239141cc406Sopenharmony_ci } 240141cc406Sopenharmony_ci if((*min+1) >= *max ) 241141cc406Sopenharmony_ci return 0; 242141cc406Sopenharmony_ci 243141cc406Sopenharmony_ci return 1; 244141cc406Sopenharmony_ci} 245141cc406Sopenharmony_ci 246141cc406Sopenharmony_ci/** cano_AdjustLightsource 247141cc406Sopenharmony_ci * coarse calibration step 0 248141cc406Sopenharmony_ci * [Monty changes]: On the CanoScan at least, the default lamp 249141cc406Sopenharmony_ci * settings are several *hundred* percent too high and vary from 250141cc406Sopenharmony_ci * scanner-to-scanner by 20-50%. This is only for CIS devices 251141cc406Sopenharmony_ci * where the lamp_off parameter is adjustable; I'd make it more general, 252141cc406Sopenharmony_ci * but I only have the CIS hardware to test. 253141cc406Sopenharmony_ci */ 254141cc406Sopenharmony_cistatic int 255141cc406Sopenharmony_cicano_AdjustLightsource( Plustek_Device *dev ) 256141cc406Sopenharmony_ci{ 257141cc406Sopenharmony_ci char tmp[40]; 258141cc406Sopenharmony_ci int i; 259141cc406Sopenharmony_ci int res_r, res_g, res_b; 260141cc406Sopenharmony_ci u_long dw, dwR, dwG, dwB, dwDiv, dwLoop1, dwLoop2; 261141cc406Sopenharmony_ci RGBUShortDef max_rgb, min_rgb, tmp_rgb; 262141cc406Sopenharmony_ci u_long *scanbuf = dev->scanning.pScanBuffer; 263141cc406Sopenharmony_ci DCapsDef *scaps = &dev->usbDev.Caps; 264141cc406Sopenharmony_ci HWDef *hw = &dev->usbDev.HwSetting; 265141cc406Sopenharmony_ci 266141cc406Sopenharmony_ci if( usb_IsEscPressed()) 267141cc406Sopenharmony_ci return SANE_FALSE; 268141cc406Sopenharmony_ci 269141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustLightsource()\n" ); 270141cc406Sopenharmony_ci 271141cc406Sopenharmony_ci if( !usb_IsCISDevice(dev)) { 272141cc406Sopenharmony_ci DBG( _DBG_INFO, "- function skipped, CCD device!\n" ); 273141cc406Sopenharmony_ci 274141cc406Sopenharmony_ci /* HEINER: we might have to tweak the PWM for the lamps */ 275141cc406Sopenharmony_ci return SANE_TRUE; 276141cc406Sopenharmony_ci } 277141cc406Sopenharmony_ci 278141cc406Sopenharmony_ci /* define the strip to scan for coarse calibration 279141cc406Sopenharmony_ci * done at optical resolution. 280141cc406Sopenharmony_ci */ 281141cc406Sopenharmony_ci m_ScanParam.Size.dwLines = 1; 282141cc406Sopenharmony_ci m_ScanParam.Size.dwPixels = scaps->Normal.Size.x * 283141cc406Sopenharmony_ci scaps->OpticDpi.x / 300UL; 284141cc406Sopenharmony_ci 285141cc406Sopenharmony_ci m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2; 286141cc406Sopenharmony_ci 287141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) 288141cc406Sopenharmony_ci m_ScanParam.Size.dwBytes *=3; 289141cc406Sopenharmony_ci 290141cc406Sopenharmony_ci m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart * 291141cc406Sopenharmony_ci 300UL / scaps->OpticDpi.x); 292141cc406Sopenharmony_ci m_ScanParam.bCalibration = PARAM_Gain; 293141cc406Sopenharmony_ci 294141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* Coarse Calibration Strip:\n" ); 295141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* Lines = %lu\n", m_ScanParam.Size.dwLines ); 296141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* Pixels = %lu\n", m_ScanParam.Size.dwPixels ); 297141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* Bytes = %lu\n", m_ScanParam.Size.dwBytes ); 298141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* Origin.X = %u\n", m_ScanParam.Origin.x ); 299141cc406Sopenharmony_ci 300141cc406Sopenharmony_ci /* init... */ 301141cc406Sopenharmony_ci max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0x3fff; 302141cc406Sopenharmony_ci min_rgb.Red = hw->red_lamp_on; 303141cc406Sopenharmony_ci min_rgb.Green = hw->green_lamp_on; 304141cc406Sopenharmony_ci min_rgb.Blue = hw->blue_lamp_on; 305141cc406Sopenharmony_ci 306141cc406Sopenharmony_ci if((dev->adj.rlampoff != -1) && 307141cc406Sopenharmony_ci (dev->adj.glampoff != -1) && (dev->adj.blampoff != -1)) { 308141cc406Sopenharmony_ci DBG( _DBG_INFO, "- function skipped, using frontend values!\n" ); 309141cc406Sopenharmony_ci return SANE_TRUE; 310141cc406Sopenharmony_ci } 311141cc406Sopenharmony_ci 312141cc406Sopenharmony_ci /* we probably should preset gain to some reasonably good value 313141cc406Sopenharmony_ci * i.e. 0x0a as it's done by Canon within their Windoze driver! 314141cc406Sopenharmony_ci */ 315141cc406Sopenharmony_ci#ifdef _TWEAK_GAIN 316141cc406Sopenharmony_ci for( i=0x3b; i<0x3e; i++ ) 317141cc406Sopenharmony_ci dev->usbDev.a_bRegs[i] = 0x0a; 318141cc406Sopenharmony_ci#endif 319141cc406Sopenharmony_ci for( i = 0; ; i++ ) { 320141cc406Sopenharmony_ci 321141cc406Sopenharmony_ci m_ScanParam.dMCLK = dMCLK; 322141cc406Sopenharmony_ci if( !usb_SetScanParameters( dev, &m_ScanParam )) { 323141cc406Sopenharmony_ci return SANE_FALSE; 324141cc406Sopenharmony_ci } 325141cc406Sopenharmony_ci 326141cc406Sopenharmony_ci if( !usb_ScanBegin( dev, SANE_FALSE) || 327141cc406Sopenharmony_ci !usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwPhyBytes ) || 328141cc406Sopenharmony_ci !usb_ScanEnd( dev )) { 329141cc406Sopenharmony_ci DBG( _DBG_ERROR, "* cano_AdjustLightsource() failed\n" ); 330141cc406Sopenharmony_ci return SANE_FALSE; 331141cc406Sopenharmony_ci } 332141cc406Sopenharmony_ci 333141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes ); 334141cc406Sopenharmony_ci DBG( _DBG_INFO2, "* PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels); 335141cc406Sopenharmony_ci 336141cc406Sopenharmony_ci sprintf( tmp, "coarse-lamp-%u.raw", i ); 337141cc406Sopenharmony_ci 338141cc406Sopenharmony_ci dumpPicInit(&m_ScanParam, tmp); 339141cc406Sopenharmony_ci dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 340141cc406Sopenharmony_ci 341141cc406Sopenharmony_ci if(usb_HostSwap()) 342141cc406Sopenharmony_ci usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes ); 343141cc406Sopenharmony_ci 344141cc406Sopenharmony_ci sprintf( tmp, "coarse-lamp-swap%u.raw", i ); 345141cc406Sopenharmony_ci 346141cc406Sopenharmony_ci dumpPicInit(&m_ScanParam, tmp); 347141cc406Sopenharmony_ci dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 348141cc406Sopenharmony_ci 349141cc406Sopenharmony_ci dwDiv = 10; 350141cc406Sopenharmony_ci dwLoop1 = m_ScanParam.Size.dwPhyPixels/dwDiv; 351141cc406Sopenharmony_ci 352141cc406Sopenharmony_ci tmp_rgb.Red = tmp_rgb.Green = tmp_rgb.Blue = 0; 353141cc406Sopenharmony_ci 354141cc406Sopenharmony_ci /* find out the max pixel value for R, G, B */ 355141cc406Sopenharmony_ci for( dw = 0; dwLoop1; dwLoop1-- ) { 356141cc406Sopenharmony_ci 357141cc406Sopenharmony_ci /* do some averaging... */ 358141cc406Sopenharmony_ci for (dwLoop2 = dwDiv, dwR = dwG = dwB = 0; dwLoop2; dwLoop2--, dw++) { 359141cc406Sopenharmony_ci 360141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 361141cc406Sopenharmony_ci 362141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) { 363141cc406Sopenharmony_ci dwR += ((u_short*)scanbuf)[dw]; 364141cc406Sopenharmony_ci dwG += ((u_short*)scanbuf) 365141cc406Sopenharmony_ci [dw+m_ScanParam.Size.dwPhyPixels+1]; 366141cc406Sopenharmony_ci dwB += ((u_short*)scanbuf) 367141cc406Sopenharmony_ci [dw+(m_ScanParam.Size.dwPhyPixels+1)*2]; 368141cc406Sopenharmony_ci } else { 369141cc406Sopenharmony_ci dwR += ((RGBUShortDef*)scanbuf)[dw].Red; 370141cc406Sopenharmony_ci dwG += ((RGBUShortDef*)scanbuf)[dw].Green; 371141cc406Sopenharmony_ci dwB += ((RGBUShortDef*)scanbuf)[dw].Blue; 372141cc406Sopenharmony_ci } 373141cc406Sopenharmony_ci } else { 374141cc406Sopenharmony_ci dwG += ((u_short*)scanbuf)[dw]; 375141cc406Sopenharmony_ci } 376141cc406Sopenharmony_ci } 377141cc406Sopenharmony_ci 378141cc406Sopenharmony_ci dwR = dwR / dwDiv; 379141cc406Sopenharmony_ci dwG = dwG / dwDiv; 380141cc406Sopenharmony_ci dwB = dwB / dwDiv; 381141cc406Sopenharmony_ci 382141cc406Sopenharmony_ci if( tmp_rgb.Red < dwR ) 383141cc406Sopenharmony_ci tmp_rgb.Red = dwR; 384141cc406Sopenharmony_ci if( tmp_rgb.Green < dwG ) 385141cc406Sopenharmony_ci tmp_rgb.Green = dwG; 386141cc406Sopenharmony_ci if( tmp_rgb.Blue < dwB ) 387141cc406Sopenharmony_ci tmp_rgb.Blue = dwB; 388141cc406Sopenharmony_ci } 389141cc406Sopenharmony_ci 390141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 391141cc406Sopenharmony_ci DBG( _DBG_INFO2, "red_lamp_off = %u/%u/%u\n", 392141cc406Sopenharmony_ci min_rgb.Red ,hw->red_lamp_off, max_rgb.Red ); 393141cc406Sopenharmony_ci } 394141cc406Sopenharmony_ci 395141cc406Sopenharmony_ci DBG( _DBG_INFO2, "green_lamp_off = %u/%u/%u\n", 396141cc406Sopenharmony_ci min_rgb.Green, hw->green_lamp_off, max_rgb.Green ); 397141cc406Sopenharmony_ci 398141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 399141cc406Sopenharmony_ci DBG( _DBG_INFO2, "blue_lamp_off = %u/%u/%u\n", 400141cc406Sopenharmony_ci min_rgb.Blue, hw->blue_lamp_off, max_rgb.Blue ); 401141cc406Sopenharmony_ci } 402141cc406Sopenharmony_ci 403141cc406Sopenharmony_ci DBG(_DBG_INFO2, "CUR(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n", 404141cc406Sopenharmony_ci tmp_rgb.Red, tmp_rgb.Red, tmp_rgb.Green, 405141cc406Sopenharmony_ci tmp_rgb.Green, tmp_rgb.Blue, tmp_rgb.Blue ); 406141cc406Sopenharmony_ci res_r = 0; 407141cc406Sopenharmony_ci res_g = 0; 408141cc406Sopenharmony_ci res_b = 0; 409141cc406Sopenharmony_ci 410141cc406Sopenharmony_ci /* bisect */ 411141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 412141cc406Sopenharmony_ci res_r = cano_adjLampSetting( &min_rgb.Red, &max_rgb.Red, 413141cc406Sopenharmony_ci &hw->red_lamp_off, tmp_rgb.Red ); 414141cc406Sopenharmony_ci res_b = cano_adjLampSetting( &min_rgb.Blue, &max_rgb.Blue, 415141cc406Sopenharmony_ci &hw->blue_lamp_off,tmp_rgb.Blue ); 416141cc406Sopenharmony_ci } 417141cc406Sopenharmony_ci 418141cc406Sopenharmony_ci res_g = cano_adjLampSetting( &min_rgb.Green, &max_rgb.Green, 419141cc406Sopenharmony_ci &hw->green_lamp_off, tmp_rgb.Green ); 420141cc406Sopenharmony_ci 421141cc406Sopenharmony_ci /* nothing adjusted, so stop here */ 422141cc406Sopenharmony_ci if((res_r == 0) && (res_g == 0) && (res_b == 0)) 423141cc406Sopenharmony_ci break; 424141cc406Sopenharmony_ci 425141cc406Sopenharmony_ci /* no need to adjust more, we have already reached the limit 426141cc406Sopenharmony_ci * without tweaking the gain. 427141cc406Sopenharmony_ci */ 428141cc406Sopenharmony_ci if((res_r == 10) && (res_g == 10) && (res_b == 10)) 429141cc406Sopenharmony_ci break; 430141cc406Sopenharmony_ci 431141cc406Sopenharmony_ci /* we raise the gain for channels, that have been limited */ 432141cc406Sopenharmony_ci#ifdef _TWEAK_GAIN 433141cc406Sopenharmony_ci if( res_r == 10 ) { 434141cc406Sopenharmony_ci if( dev->usbDev.a_bRegs[0x3b] < 0xf) 435141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3b]++; 436141cc406Sopenharmony_ci } 437141cc406Sopenharmony_ci if( res_g == 10 ) { 438141cc406Sopenharmony_ci if( dev->usbDev.a_bRegs[0x3c] < 0x0f) 439141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3c]++; 440141cc406Sopenharmony_ci } 441141cc406Sopenharmony_ci if( res_b == 10 ) { 442141cc406Sopenharmony_ci if( dev->usbDev.a_bRegs[0x3d] < 0x0f) 443141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3d]++; 444141cc406Sopenharmony_ci } 445141cc406Sopenharmony_ci#endif 446141cc406Sopenharmony_ci 447141cc406Sopenharmony_ci /* now decide what to do: 448141cc406Sopenharmony_ci * if we were too bright, we have to rerun the loop in any 449141cc406Sopenharmony_ci * case 450141cc406Sopenharmony_ci * if we're too dark, we should rerun it too, but we can 451141cc406Sopenharmony_ci * compensate that using higher gain values later 452141cc406Sopenharmony_ci */ 453141cc406Sopenharmony_ci if( i >= 10 ) { 454141cc406Sopenharmony_ci DBG(_DBG_INFO, "* 10 times limit reached, still too dark!!!\n"); 455141cc406Sopenharmony_ci break; 456141cc406Sopenharmony_ci } 457141cc406Sopenharmony_ci usb_AdjustLamps(dev, SANE_TRUE); 458141cc406Sopenharmony_ci } 459141cc406Sopenharmony_ci 460141cc406Sopenharmony_ci DBG( _DBG_INFO, "* red_lamp_on = %u\n", hw->red_lamp_on ); 461141cc406Sopenharmony_ci DBG( _DBG_INFO, "* red_lamp_off = %u\n", hw->red_lamp_off ); 462141cc406Sopenharmony_ci DBG( _DBG_INFO, "* green_lamp_on = %u\n", hw->green_lamp_on ); 463141cc406Sopenharmony_ci DBG( _DBG_INFO, "* green_lamp_off = %u\n", hw->green_lamp_off ); 464141cc406Sopenharmony_ci DBG( _DBG_INFO, "* blue_lamp_on = %u\n", hw->blue_lamp_on ); 465141cc406Sopenharmony_ci DBG( _DBG_INFO, "* blue_lamp_off = %u\n", hw->blue_lamp_off ); 466141cc406Sopenharmony_ci 467141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustLightsource() done.\n" ); 468141cc406Sopenharmony_ci return SANE_TRUE; 469141cc406Sopenharmony_ci} 470141cc406Sopenharmony_ci 471141cc406Sopenharmony_ci/** 472141cc406Sopenharmony_ci */ 473141cc406Sopenharmony_cistatic int 474141cc406Sopenharmony_cicano_adjGainSetting( u_char *min, u_char *max, u_char *gain,u_long val ) 475141cc406Sopenharmony_ci{ 476141cc406Sopenharmony_ci u_long newgain = *gain; 477141cc406Sopenharmony_ci 478141cc406Sopenharmony_ci if((val < IDEAL_GainNormal) && (val > (IDEAL_GainNormal-8000))) 479141cc406Sopenharmony_ci return 0; 480141cc406Sopenharmony_ci 481141cc406Sopenharmony_ci if(val > (IDEAL_GainNormal-4000)) { 482141cc406Sopenharmony_ci *max = newgain; 483141cc406Sopenharmony_ci *gain = (newgain + *min)>>1; 484141cc406Sopenharmony_ci } else { 485141cc406Sopenharmony_ci *min = newgain; 486141cc406Sopenharmony_ci *gain = (newgain + *max)>>1; 487141cc406Sopenharmony_ci } 488141cc406Sopenharmony_ci 489141cc406Sopenharmony_ci if((*min+1) >= *max) 490141cc406Sopenharmony_ci return 0; 491141cc406Sopenharmony_ci 492141cc406Sopenharmony_ci return 1; 493141cc406Sopenharmony_ci} 494141cc406Sopenharmony_ci 495141cc406Sopenharmony_ci/** cano_AdjustGain 496141cc406Sopenharmony_ci * function to perform the "coarse calibration step" part 1. 497141cc406Sopenharmony_ci * We scan reference image pixels to determine the optimum coarse gain settings 498141cc406Sopenharmony_ci * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are 499141cc406Sopenharmony_ci * applied at the line rate during normal scanning. 500141cc406Sopenharmony_ci * The scanned line should contain a white strip with some black at the 501141cc406Sopenharmony_ci * beginning. The function searches for the maximum value which corresponds to 502141cc406Sopenharmony_ci * the maximum white value. 503141cc406Sopenharmony_ci * Affects register 0x3b, 0x3c and 0x3d 504141cc406Sopenharmony_ci * 505141cc406Sopenharmony_ci * adjLightsource, above, steals most of this function's thunder. 506141cc406Sopenharmony_ci */ 507141cc406Sopenharmony_cistatic SANE_Bool 508141cc406Sopenharmony_cicano_AdjustGain( Plustek_Device *dev ) 509141cc406Sopenharmony_ci{ 510141cc406Sopenharmony_ci char tmp[40]; 511141cc406Sopenharmony_ci int i = 0, adj = 1; 512141cc406Sopenharmony_ci u_long dw; 513141cc406Sopenharmony_ci u_long *scanbuf = dev->scanning.pScanBuffer; 514141cc406Sopenharmony_ci DCapsDef *scaps = &dev->usbDev.Caps; 515141cc406Sopenharmony_ci HWDef *hw = &dev->usbDev.HwSetting; 516141cc406Sopenharmony_ci 517141cc406Sopenharmony_ci unsigned char max[3], min[3]; 518141cc406Sopenharmony_ci 519141cc406Sopenharmony_ci if( usb_IsEscPressed()) 520141cc406Sopenharmony_ci return SANE_FALSE; 521141cc406Sopenharmony_ci 522141cc406Sopenharmony_ci bMaxITA = 0xff; 523141cc406Sopenharmony_ci 524141cc406Sopenharmony_ci max[0] = max[1] = max[2] = 0x3f; 525141cc406Sopenharmony_ci min[0] = min[1] = min[2] = 1; 526141cc406Sopenharmony_ci 527141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustGain()\n" ); 528141cc406Sopenharmony_ci if( !usb_InCalibrationMode(dev)) { 529141cc406Sopenharmony_ci if((dev->adj.rgain != -1) && 530141cc406Sopenharmony_ci (dev->adj.ggain != -1) && (dev->adj.bgain != -1)) { 531141cc406Sopenharmony_ci setAdjGain( dev->adj.rgain, &dev->usbDev.a_bRegs[0x3b] ); 532141cc406Sopenharmony_ci setAdjGain( dev->adj.ggain, &dev->usbDev.a_bRegs[0x3c] ); 533141cc406Sopenharmony_ci setAdjGain( dev->adj.bgain, &dev->usbDev.a_bRegs[0x3d] ); 534141cc406Sopenharmony_ci DBG( _DBG_INFO, "- function skipped, using frontend values!\n" ); 535141cc406Sopenharmony_ci return SANE_TRUE; 536141cc406Sopenharmony_ci } 537141cc406Sopenharmony_ci } 538141cc406Sopenharmony_ci 539141cc406Sopenharmony_ci /* define the strip to scan for coarse calibration 540141cc406Sopenharmony_ci * done at 300dpi 541141cc406Sopenharmony_ci */ 542141cc406Sopenharmony_ci m_ScanParam.Size.dwLines = 1; /* for gain */ 543141cc406Sopenharmony_ci m_ScanParam.Size.dwPixels = scaps->Normal.Size.x * 544141cc406Sopenharmony_ci scaps->OpticDpi.x / 300UL; 545141cc406Sopenharmony_ci 546141cc406Sopenharmony_ci m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2; 547141cc406Sopenharmony_ci 548141cc406Sopenharmony_ci if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color) 549141cc406Sopenharmony_ci m_ScanParam.Size.dwBytes *=3; 550141cc406Sopenharmony_ci 551141cc406Sopenharmony_ci m_ScanParam.Origin.x = (u_short)((u_long) hw->wActivePixelsStart * 552141cc406Sopenharmony_ci 300UL / scaps->OpticDpi.x); 553141cc406Sopenharmony_ci m_ScanParam.bCalibration = PARAM_Gain; 554141cc406Sopenharmony_ci 555141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Coarse Calibration Strip:\n" ); 556141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines ); 557141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels ); 558141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes ); 559141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x ); 560141cc406Sopenharmony_ci 561141cc406Sopenharmony_ci while( adj ) { 562141cc406Sopenharmony_ci 563141cc406Sopenharmony_ci m_ScanParam.dMCLK = dMCLK; 564141cc406Sopenharmony_ci 565141cc406Sopenharmony_ci if( !usb_SetScanParameters( dev, &m_ScanParam )) 566141cc406Sopenharmony_ci return SANE_FALSE; 567141cc406Sopenharmony_ci 568141cc406Sopenharmony_ci if( !usb_ScanBegin( dev, SANE_FALSE) || 569141cc406Sopenharmony_ci !usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes) || 570141cc406Sopenharmony_ci !usb_ScanEnd( dev )) { 571141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_AdjustGain() failed\n" ); 572141cc406Sopenharmony_ci return SANE_FALSE; 573141cc406Sopenharmony_ci } 574141cc406Sopenharmony_ci 575141cc406Sopenharmony_ci DBG( _DBG_INFO2, "PhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes ); 576141cc406Sopenharmony_ci DBG( _DBG_INFO2, "PhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels ); 577141cc406Sopenharmony_ci 578141cc406Sopenharmony_ci sprintf( tmp, "coarse-gain-%u.raw", i++ ); 579141cc406Sopenharmony_ci 580141cc406Sopenharmony_ci dumpPicInit(&m_ScanParam, tmp); 581141cc406Sopenharmony_ci dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 582141cc406Sopenharmony_ci 583141cc406Sopenharmony_ci if(usb_HostSwap()) 584141cc406Sopenharmony_ci usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes ); 585141cc406Sopenharmony_ci 586141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 587141cc406Sopenharmony_ci 588141cc406Sopenharmony_ci RGBUShortDef max_rgb; 589141cc406Sopenharmony_ci u_long dwR, dwG, dwB; 590141cc406Sopenharmony_ci u_long dwDiv = 10; 591141cc406Sopenharmony_ci u_long dwLoop1 = m_ScanParam.Size.dwPhyPixels/dwDiv, dwLoop2; 592141cc406Sopenharmony_ci 593141cc406Sopenharmony_ci max_rgb.Red = max_rgb.Green = max_rgb.Blue = 0; 594141cc406Sopenharmony_ci 595141cc406Sopenharmony_ci /* find out the max pixel value for R, G, B */ 596141cc406Sopenharmony_ci for( dw = 0; dwLoop1; dwLoop1-- ) { 597141cc406Sopenharmony_ci 598141cc406Sopenharmony_ci /* do some averaging... */ 599141cc406Sopenharmony_ci for (dwLoop2 = dwDiv, dwR=dwG=dwB=0; dwLoop2; dwLoop2--, dw++) { 600141cc406Sopenharmony_ci 601141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) { 602141cc406Sopenharmony_ci dwR += ((u_short*)scanbuf)[dw]; 603141cc406Sopenharmony_ci dwG += ((u_short*)scanbuf) 604141cc406Sopenharmony_ci [dw+m_ScanParam.Size.dwPhyPixels+1]; 605141cc406Sopenharmony_ci dwB += ((u_short*)scanbuf) 606141cc406Sopenharmony_ci [dw+(m_ScanParam.Size.dwPhyPixels+1)*2]; 607141cc406Sopenharmony_ci } else { 608141cc406Sopenharmony_ci dwR += ((RGBUShortDef*)scanbuf)[dw].Red; 609141cc406Sopenharmony_ci dwG += ((RGBUShortDef*)scanbuf)[dw].Green; 610141cc406Sopenharmony_ci dwB += ((RGBUShortDef*)scanbuf)[dw].Blue; 611141cc406Sopenharmony_ci } 612141cc406Sopenharmony_ci } 613141cc406Sopenharmony_ci dwR = dwR / dwDiv; 614141cc406Sopenharmony_ci dwG = dwG / dwDiv; 615141cc406Sopenharmony_ci dwB = dwB / dwDiv; 616141cc406Sopenharmony_ci 617141cc406Sopenharmony_ci if(max_rgb.Red < dwR) 618141cc406Sopenharmony_ci max_rgb.Red = dwR; 619141cc406Sopenharmony_ci if(max_rgb.Green < dwG) 620141cc406Sopenharmony_ci max_rgb.Green = dwG; 621141cc406Sopenharmony_ci if(max_rgb.Blue < dwB) 622141cc406Sopenharmony_ci max_rgb.Blue = dwB; 623141cc406Sopenharmony_ci } 624141cc406Sopenharmony_ci 625141cc406Sopenharmony_ci DBG(_DBG_INFO2, "MAX(R,G,B)= 0x%04x(%u), 0x%04x(%u), 0x%04x(%u)\n", 626141cc406Sopenharmony_ci max_rgb.Red, max_rgb.Red, max_rgb.Green, 627141cc406Sopenharmony_ci max_rgb.Green, max_rgb.Blue, max_rgb.Blue ); 628141cc406Sopenharmony_ci 629141cc406Sopenharmony_ci adj = cano_adjGainSetting(min , max ,dev->usbDev.a_bRegs+0x3b,max_rgb.Red ); 630141cc406Sopenharmony_ci adj += cano_adjGainSetting(min+1, max+1,dev->usbDev.a_bRegs+0x3c,max_rgb.Green); 631141cc406Sopenharmony_ci adj += cano_adjGainSetting(min+2, max+2,dev->usbDev.a_bRegs+0x3d,max_rgb.Blue ); 632141cc406Sopenharmony_ci 633141cc406Sopenharmony_ci } else { 634141cc406Sopenharmony_ci 635141cc406Sopenharmony_ci u_short w_max = 0; 636141cc406Sopenharmony_ci 637141cc406Sopenharmony_ci for( dw = 0; dw < m_ScanParam.Size.dwPhyPixels; dw++ ) { 638141cc406Sopenharmony_ci if( w_max < ((u_short*)scanbuf)[dw]) 639141cc406Sopenharmony_ci w_max = ((u_short*)scanbuf)[dw]; 640141cc406Sopenharmony_ci } 641141cc406Sopenharmony_ci 642141cc406Sopenharmony_ci adj = cano_adjGainSetting(min,max,dev->usbDev.a_bRegs+0x3c,w_max); 643141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3b] = (dev->usbDev.a_bRegs[0x3d] = dev->usbDev.a_bRegs[0x3c]); 644141cc406Sopenharmony_ci 645141cc406Sopenharmony_ci DBG(_DBG_INFO2, "MAX(G)= 0x%04x(%u)\n", w_max, w_max ); 646141cc406Sopenharmony_ci 647141cc406Sopenharmony_ci } 648141cc406Sopenharmony_ci DBG( _DBG_INFO2, "REG[0x3b] = %u\n", dev->usbDev.a_bRegs[0x3b] ); 649141cc406Sopenharmony_ci DBG( _DBG_INFO2, "REG[0x3c] = %u\n", dev->usbDev.a_bRegs[0x3c] ); 650141cc406Sopenharmony_ci DBG( _DBG_INFO2, "REG[0x3d] = %u\n", dev->usbDev.a_bRegs[0x3d] ); 651141cc406Sopenharmony_ci } 652141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustGain() done.\n" ); 653141cc406Sopenharmony_ci return SANE_TRUE; 654141cc406Sopenharmony_ci} 655141cc406Sopenharmony_ci 656141cc406Sopenharmony_cistatic int tweak_offset[3]; 657141cc406Sopenharmony_ci 658141cc406Sopenharmony_ci/** 659141cc406Sopenharmony_ci */ 660141cc406Sopenharmony_cistatic int 661141cc406Sopenharmony_cicano_GetNewOffset(Plustek_Device *dev, u_long *val, int channel, signed char *low, 662141cc406Sopenharmony_ci signed char *now, signed char *high, u_long *zc) 663141cc406Sopenharmony_ci{ 664141cc406Sopenharmony_ci DCapsDef *scaps = &dev->usbDev.Caps; 665141cc406Sopenharmony_ci 666141cc406Sopenharmony_ci if (tweak_offset[channel]) { 667141cc406Sopenharmony_ci 668141cc406Sopenharmony_ci /* if we're too black, we're likely off the low end */ 669141cc406Sopenharmony_ci if( val[channel] <= 16 ) { 670141cc406Sopenharmony_ci low[channel] = now[channel]; 671141cc406Sopenharmony_ci now[channel] = (now[channel]+high[channel])/2; 672141cc406Sopenharmony_ci 673141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 674141cc406Sopenharmony_ci 675141cc406Sopenharmony_ci if( low[channel]+1 >= high[channel] ) 676141cc406Sopenharmony_ci return 0; 677141cc406Sopenharmony_ci return 1; 678141cc406Sopenharmony_ci 679141cc406Sopenharmony_ci } else if ( val[channel]>=2048 ) { 680141cc406Sopenharmony_ci high[channel]=now[channel]; 681141cc406Sopenharmony_ci now[channel]=(now[channel]+low[channel])/2; 682141cc406Sopenharmony_ci 683141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 684141cc406Sopenharmony_ci 685141cc406Sopenharmony_ci if(low[channel]+1>=high[channel]) 686141cc406Sopenharmony_ci return 0; 687141cc406Sopenharmony_ci return 1; 688141cc406Sopenharmony_ci } 689141cc406Sopenharmony_ci } 690141cc406Sopenharmony_ci 691141cc406Sopenharmony_ci if (!(scaps->workaroundFlag & _WAF_INC_DARKTGT)) { 692141cc406Sopenharmony_ci DBG( _DBG_INFO, "0 Pixel adjustment not active!\n"); 693141cc406Sopenharmony_ci return 0; 694141cc406Sopenharmony_ci } 695141cc406Sopenharmony_ci 696141cc406Sopenharmony_ci /* reaching this point, our black level should be okay, but 697141cc406Sopenharmony_ci * we also should check the percentage of 0 level pixels. 698141cc406Sopenharmony_ci * It turned out, that when having a lot of 0 level pixels, 699141cc406Sopenharmony_ci * the calibration will be bad and the resulting scans show up 700141cc406Sopenharmony_ci * stripes... 701141cc406Sopenharmony_ci */ 702141cc406Sopenharmony_ci if (zc[channel] > _DARK_TGT_THRESH) { 703141cc406Sopenharmony_ci DBG( _DBG_INFO2, "More than %u%% 0 pixels detected, raise offset!\n", 704141cc406Sopenharmony_ci _DARK_TGT_THRESH); 705141cc406Sopenharmony_ci high[channel]=now[channel]; 706141cc406Sopenharmony_ci now[channel]=(now[channel]+low[channel])/2; 707141cc406Sopenharmony_ci 708141cc406Sopenharmony_ci /* no more value checks, the goal to set the black level < 2048 709141cc406Sopenharmony_ci * will cause stripes... 710141cc406Sopenharmony_ci */ 711141cc406Sopenharmony_ci tweak_offset[channel] = 0; 712141cc406Sopenharmony_ci 713141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 714141cc406Sopenharmony_ci 715141cc406Sopenharmony_ci if( low[channel]+1 >= high[channel] ) 716141cc406Sopenharmony_ci return 0; 717141cc406Sopenharmony_ci return 1; 718141cc406Sopenharmony_ci 719141cc406Sopenharmony_ci } 720141cc406Sopenharmony_ci#if 0 721141cc406Sopenharmony_ci else if ( val[channel]>=4096 ) { 722141cc406Sopenharmony_ci low[channel] = now[channel]; 723141cc406Sopenharmony_ci now[channel] = (now[channel]+high[channel])/2; 724141cc406Sopenharmony_ci 725141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38+channel]= (now[channel]&0x3f); 726141cc406Sopenharmony_ci 727141cc406Sopenharmony_ci if( low[channel]+1 >= high[channel] ) 728141cc406Sopenharmony_ci return 0; 729141cc406Sopenharmony_ci return 1; 730141cc406Sopenharmony_ci } 731141cc406Sopenharmony_ci#endif 732141cc406Sopenharmony_ci return 0; 733141cc406Sopenharmony_ci} 734141cc406Sopenharmony_ci 735141cc406Sopenharmony_ci/** cano_AdjustOffset 736141cc406Sopenharmony_ci * function to perform the "coarse calibration step" part 2. 737141cc406Sopenharmony_ci * We scan reference image pixels to determine the optimum coarse offset settings 738141cc406Sopenharmony_ci * for R, G, B. (Analog gain and offset prior to ADC). These coefficients are 739141cc406Sopenharmony_ci * applied at the line rate during normal scanning. 740141cc406Sopenharmony_ci * On CIS based devices, we switch the light off, on CCD devices, we use the optical 741141cc406Sopenharmony_ci * black pixels. 742141cc406Sopenharmony_ci * Affects register 0x38, 0x39 and 0x3a 743141cc406Sopenharmony_ci */ 744141cc406Sopenharmony_ci 745141cc406Sopenharmony_ci/* Move this to a bisection-based algo and correct some fenceposts; 746141cc406Sopenharmony_ci Plustek's example code disagrees with NatSemi's docs; going by the 747141cc406Sopenharmony_ci docs works better, I will assume the docs are correct. --Monty */ 748141cc406Sopenharmony_ci 749141cc406Sopenharmony_cistatic int 750141cc406Sopenharmony_cicano_AdjustOffset( Plustek_Device *dev ) 751141cc406Sopenharmony_ci{ 752141cc406Sopenharmony_ci char tmp[40]; 753141cc406Sopenharmony_ci int i, adj; 754141cc406Sopenharmony_ci u_short r, g, b; 755141cc406Sopenharmony_ci u_long dw, dwPixels; 756141cc406Sopenharmony_ci u_long dwSum[3], zCount[3]; 757141cc406Sopenharmony_ci 758141cc406Sopenharmony_ci signed char low[3] = {-32,-32,-32 }; 759141cc406Sopenharmony_ci signed char now[3] = { 0, 0, 0 }; 760141cc406Sopenharmony_ci signed char high[3] = { 31, 31, 31 }; 761141cc406Sopenharmony_ci 762141cc406Sopenharmony_ci u_long *scanbuf = dev->scanning.pScanBuffer; 763141cc406Sopenharmony_ci HWDef *hw = &dev->usbDev.HwSetting; 764141cc406Sopenharmony_ci DCapsDef *scaps = &dev->usbDev.Caps; 765141cc406Sopenharmony_ci 766141cc406Sopenharmony_ci if( usb_IsEscPressed()) 767141cc406Sopenharmony_ci return SANE_FALSE; 768141cc406Sopenharmony_ci 769141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustOffset()\n" ); 770141cc406Sopenharmony_ci if( !usb_InCalibrationMode(dev)) { 771141cc406Sopenharmony_ci if((dev->adj.rofs != -1) && 772141cc406Sopenharmony_ci (dev->adj.gofs != -1) && (dev->adj.bofs != -1)) { 773141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38] = (dev->adj.rofs & 0x3f); 774141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x39] = (dev->adj.gofs & 0x3f); 775141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3a] = (dev->adj.bofs & 0x3f); 776141cc406Sopenharmony_ci DBG( _DBG_INFO, "- function skipped, using frontend values!\n" ); 777141cc406Sopenharmony_ci return SANE_TRUE; 778141cc406Sopenharmony_ci } 779141cc406Sopenharmony_ci } 780141cc406Sopenharmony_ci 781141cc406Sopenharmony_ci m_ScanParam.Size.dwLines = 1; 782141cc406Sopenharmony_ci m_ScanParam.Size.dwPixels = scaps->Normal.Size.x*scaps->OpticDpi.x/300UL; 783141cc406Sopenharmony_ci 784141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) 785141cc406Sopenharmony_ci dwPixels = m_ScanParam.Size.dwPixels; 786141cc406Sopenharmony_ci else 787141cc406Sopenharmony_ci dwPixels = (u_long)(hw->bOpticBlackEnd - hw->bOpticBlackStart); 788141cc406Sopenharmony_ci 789141cc406Sopenharmony_ci m_ScanParam.Size.dwBytes = m_ScanParam.Size.dwPixels * 2; 790141cc406Sopenharmony_ci 791141cc406Sopenharmony_ci if( usb_IsCISDevice(dev) && m_ScanParam.bDataType == SCANDATATYPE_Color) 792141cc406Sopenharmony_ci m_ScanParam.Size.dwBytes *= 3; 793141cc406Sopenharmony_ci 794141cc406Sopenharmony_ci m_ScanParam.Origin.x = (u_short)((u_long)hw->bOpticBlackStart * 300UL / 795141cc406Sopenharmony_ci dev->usbDev.Caps.OpticDpi.x); 796141cc406Sopenharmony_ci m_ScanParam.bCalibration = PARAM_Offset; 797141cc406Sopenharmony_ci m_ScanParam.dMCLK = dMCLK; 798141cc406Sopenharmony_ci 799141cc406Sopenharmony_ci if( !usb_SetScanParameters( dev, &m_ScanParam )) { 800141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" ); 801141cc406Sopenharmony_ci return SANE_FALSE; 802141cc406Sopenharmony_ci } 803141cc406Sopenharmony_ci 804141cc406Sopenharmony_ci DBG( _DBG_INFO2, "S.dwPixels = %lu\n", m_ScanParam.Size.dwPixels ); 805141cc406Sopenharmony_ci DBG( _DBG_INFO2, "dwPixels = %lu\n", dwPixels ); 806141cc406Sopenharmony_ci DBG( _DBG_INFO2, "dwPhyBytes = %lu\n", m_ScanParam.Size.dwPhyBytes ); 807141cc406Sopenharmony_ci DBG( _DBG_INFO2, "dwPhyPixels = %lu\n", m_ScanParam.Size.dwPhyPixels ); 808141cc406Sopenharmony_ci 809141cc406Sopenharmony_ci tweak_offset[0] = 810141cc406Sopenharmony_ci tweak_offset[1] = 811141cc406Sopenharmony_ci tweak_offset[2] = 1; 812141cc406Sopenharmony_ci 813141cc406Sopenharmony_ci for( i = 0, adj = 1; adj != 0; i++ ) { 814141cc406Sopenharmony_ci 815141cc406Sopenharmony_ci if((!usb_ScanBegin(dev, SANE_FALSE)) || 816141cc406Sopenharmony_ci (!usb_ScanReadImage(dev,scanbuf,m_ScanParam.Size.dwPhyBytes)) || 817141cc406Sopenharmony_ci !usb_ScanEnd( dev )) { 818141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_AdjustOffset() failed\n" ); 819141cc406Sopenharmony_ci return SANE_FALSE; 820141cc406Sopenharmony_ci } 821141cc406Sopenharmony_ci 822141cc406Sopenharmony_ci sprintf( tmp, "coarse-off-%u.raw", i ); 823141cc406Sopenharmony_ci 824141cc406Sopenharmony_ci dumpPicInit(&m_ScanParam, tmp); 825141cc406Sopenharmony_ci dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwPhyBytes, 0); 826141cc406Sopenharmony_ci 827141cc406Sopenharmony_ci if(usb_HostSwap()) 828141cc406Sopenharmony_ci usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwPhyBytes ); 829141cc406Sopenharmony_ci 830141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 831141cc406Sopenharmony_ci 832141cc406Sopenharmony_ci dwSum[0] = dwSum[1] = dwSum[2] = 0; 833141cc406Sopenharmony_ci zCount[0] = zCount[1] = zCount[2] = 0; 834141cc406Sopenharmony_ci 835141cc406Sopenharmony_ci for (dw = 0; dw < dwPixels; dw++) { 836141cc406Sopenharmony_ci 837141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) { 838141cc406Sopenharmony_ci 839141cc406Sopenharmony_ci r = ((u_short*)scanbuf)[dw]; 840141cc406Sopenharmony_ci g = ((u_short*)scanbuf)[dw+m_ScanParam.Size.dwPhyPixels+1]; 841141cc406Sopenharmony_ci b = ((u_short*)scanbuf)[dw+(m_ScanParam.Size.dwPhyPixels+1)*2]; 842141cc406Sopenharmony_ci 843141cc406Sopenharmony_ci } else { 844141cc406Sopenharmony_ci r = ((RGBUShortDef*)scanbuf)[dw].Red; 845141cc406Sopenharmony_ci g = ((RGBUShortDef*)scanbuf)[dw].Green; 846141cc406Sopenharmony_ci b = ((RGBUShortDef*)scanbuf)[dw].Blue; 847141cc406Sopenharmony_ci } 848141cc406Sopenharmony_ci 849141cc406Sopenharmony_ci dwSum[0] += r; 850141cc406Sopenharmony_ci dwSum[1] += g; 851141cc406Sopenharmony_ci dwSum[2] += b; 852141cc406Sopenharmony_ci 853141cc406Sopenharmony_ci if (r==0) zCount[0]++; 854141cc406Sopenharmony_ci if (g==0) zCount[1]++; 855141cc406Sopenharmony_ci if (b==0) zCount[2]++; 856141cc406Sopenharmony_ci } 857141cc406Sopenharmony_ci 858141cc406Sopenharmony_ci DBG( _DBG_INFO2, "RedSum = %lu, ave = %lu, ZC=%lu, %lu%%\n", 859141cc406Sopenharmony_ci dwSum[0], dwSum[0]/dwPixels, 860141cc406Sopenharmony_ci zCount[0], (zCount[0]*100)/dwPixels); 861141cc406Sopenharmony_ci DBG( _DBG_INFO2, "GreenSum = %lu, ave = %lu, ZC=%lu, %lu%%\n", 862141cc406Sopenharmony_ci dwSum[1], dwSum[1]/dwPixels, 863141cc406Sopenharmony_ci zCount[1], (zCount[1]*100)/dwPixels); 864141cc406Sopenharmony_ci DBG( _DBG_INFO2, "BlueSum = %lu, ave = %lu, ZC=%lu, %lu%%\n", 865141cc406Sopenharmony_ci dwSum[2], dwSum[2]/dwPixels, 866141cc406Sopenharmony_ci zCount[2], (zCount[2]*100)/dwPixels); 867141cc406Sopenharmony_ci 868141cc406Sopenharmony_ci /* do averaging for each channel */ 869141cc406Sopenharmony_ci dwSum[0] /= dwPixels; 870141cc406Sopenharmony_ci dwSum[1] /= dwPixels; 871141cc406Sopenharmony_ci dwSum[2] /= dwPixels; 872141cc406Sopenharmony_ci 873141cc406Sopenharmony_ci zCount[0] = (zCount[0] * 100)/ dwPixels; 874141cc406Sopenharmony_ci zCount[1] = (zCount[1] * 100)/ dwPixels; 875141cc406Sopenharmony_ci zCount[2] = (zCount[2] * 100)/ dwPixels; 876141cc406Sopenharmony_ci 877141cc406Sopenharmony_ci adj = cano_GetNewOffset(dev, dwSum, 0, low, now, high, zCount); 878141cc406Sopenharmony_ci adj |= cano_GetNewOffset(dev, dwSum, 1, low, now, high, zCount); 879141cc406Sopenharmony_ci adj |= cano_GetNewOffset(dev, dwSum, 2, low, now, high, zCount); 880141cc406Sopenharmony_ci 881141cc406Sopenharmony_ci DBG( _DBG_INFO2, "RedOff = %d/%d/%d\n", 882141cc406Sopenharmony_ci (int)low[0],(int)now[0],(int)high[0]); 883141cc406Sopenharmony_ci DBG( _DBG_INFO2, "GreenOff = %d/%d/%d\n", 884141cc406Sopenharmony_ci (int)low[1],(int)now[1],(int)high[1]); 885141cc406Sopenharmony_ci DBG( _DBG_INFO2, "BlueOff = %d/%d/%d\n", 886141cc406Sopenharmony_ci (int)low[2],(int)now[2],(int)high[2]); 887141cc406Sopenharmony_ci 888141cc406Sopenharmony_ci } else { 889141cc406Sopenharmony_ci dwSum[0] = 0; 890141cc406Sopenharmony_ci zCount[0] = 0; 891141cc406Sopenharmony_ci 892141cc406Sopenharmony_ci for( dw = 0; dw < dwPixels; dw++ ) { 893141cc406Sopenharmony_ci dwSum[0] += ((u_short*)scanbuf)[dw]; 894141cc406Sopenharmony_ci 895141cc406Sopenharmony_ci if (((u_short*)scanbuf)[dw] == 0) 896141cc406Sopenharmony_ci zCount[0]++; 897141cc406Sopenharmony_ci } 898141cc406Sopenharmony_ci 899141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Sum=%lu, ave=%lu, ZC=%lu, %lu%%\n", 900141cc406Sopenharmony_ci dwSum[0],dwSum[0]/dwPixels, 901141cc406Sopenharmony_ci zCount[0], (zCount[0]*100)/dwPixels); 902141cc406Sopenharmony_ci 903141cc406Sopenharmony_ci dwSum[0] /= dwPixels; 904141cc406Sopenharmony_ci zCount[0] = (zCount[0] * 100)/ dwPixels; 905141cc406Sopenharmony_ci 906141cc406Sopenharmony_ci adj = cano_GetNewOffset(dev, dwSum, 0, low, now, high, zCount); 907141cc406Sopenharmony_ci 908141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3a] = 909141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x39] = dev->usbDev.a_bRegs[0x38]; 910141cc406Sopenharmony_ci 911141cc406Sopenharmony_ci DBG( _DBG_INFO2, "GrayOff = %d/%d/%d\n", 912141cc406Sopenharmony_ci (int)low[0],(int)now[0],(int)high[0]); 913141cc406Sopenharmony_ci } 914141cc406Sopenharmony_ci 915141cc406Sopenharmony_ci DBG( _DBG_INFO2, "REG[0x38] = %u\n", dev->usbDev.a_bRegs[0x38] ); 916141cc406Sopenharmony_ci DBG( _DBG_INFO2, "REG[0x39] = %u\n", dev->usbDev.a_bRegs[0x39] ); 917141cc406Sopenharmony_ci DBG( _DBG_INFO2, "REG[0x3a] = %u\n", dev->usbDev.a_bRegs[0x3a] ); 918141cc406Sopenharmony_ci 919141cc406Sopenharmony_ci _UIO(sanei_lm983x_write(dev->fd, 0x38, &dev->usbDev.a_bRegs[0x38], 3, SANE_TRUE)); 920141cc406Sopenharmony_ci } 921141cc406Sopenharmony_ci 922141cc406Sopenharmony_ci /* is that really needed?! */ 923141cc406Sopenharmony_ci if( m_ScanParam.bDataType == SCANDATATYPE_Color ) { 924141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38] = now[0] & 0x3f; 925141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x39] = now[1] & 0x3f; 926141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3a] = now[2] & 0x3f; 927141cc406Sopenharmony_ci } else { 928141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x38] = 929141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x39] = 930141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x3a] = now[0] & 0x3f; 931141cc406Sopenharmony_ci } 932141cc406Sopenharmony_ci 933141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustOffset() done.\n" ); 934141cc406Sopenharmony_ci return SANE_TRUE; 935141cc406Sopenharmony_ci} 936141cc406Sopenharmony_ci 937141cc406Sopenharmony_ci/** usb_AdjustDarkShading 938141cc406Sopenharmony_ci * fine calibration part 1 939141cc406Sopenharmony_ci */ 940141cc406Sopenharmony_cistatic SANE_Bool 941141cc406Sopenharmony_cicano_AdjustDarkShading( Plustek_Device *dev, u_short cal_dpi ) 942141cc406Sopenharmony_ci{ 943141cc406Sopenharmony_ci char tmp[40]; 944141cc406Sopenharmony_ci ScanParam *param = &dev->scanning.sParam; 945141cc406Sopenharmony_ci ScanDef *scan = &dev->scanning; 946141cc406Sopenharmony_ci u_long *scanbuf = scan->pScanBuffer; 947141cc406Sopenharmony_ci u_short *bufp; 948141cc406Sopenharmony_ci unsigned int i, j; 949141cc406Sopenharmony_ci int step, stepW, val; 950141cc406Sopenharmony_ci u_long red, green, blue, gray; 951141cc406Sopenharmony_ci 952141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustDarkShading()\n" ); 953141cc406Sopenharmony_ci if( usb_IsEscPressed()) 954141cc406Sopenharmony_ci return SANE_FALSE; 955141cc406Sopenharmony_ci 956141cc406Sopenharmony_ci usb_PrepareFineCal( dev, &m_ScanParam, cal_dpi ); 957141cc406Sopenharmony_ci m_ScanParam.bCalibration = PARAM_DarkShading; 958141cc406Sopenharmony_ci 959141cc406Sopenharmony_ci sprintf( tmp, "fine-dark.raw" ); 960141cc406Sopenharmony_ci dumpPicInit(&m_ScanParam, tmp); 961141cc406Sopenharmony_ci 962141cc406Sopenharmony_ci usb_SetScanParameters( dev, &m_ScanParam ); 963141cc406Sopenharmony_ci if( usb_ScanBegin( dev, SANE_FALSE ) && 964141cc406Sopenharmony_ci usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwTotalBytes)) { 965141cc406Sopenharmony_ci 966141cc406Sopenharmony_ci dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwTotalBytes, 0); 967141cc406Sopenharmony_ci 968141cc406Sopenharmony_ci if(usb_HostSwap()) 969141cc406Sopenharmony_ci usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwTotalBytes); 970141cc406Sopenharmony_ci } 971141cc406Sopenharmony_ci if (!usb_ScanEnd( dev )){ 972141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_AdjustDarkShading() failed\n" ); 973141cc406Sopenharmony_ci return SANE_FALSE; 974141cc406Sopenharmony_ci } 975141cc406Sopenharmony_ci 976141cc406Sopenharmony_ci /* average the n lines, compute reg values */ 977141cc406Sopenharmony_ci if( scan->sParam.bDataType == SCANDATATYPE_Color ) { 978141cc406Sopenharmony_ci 979141cc406Sopenharmony_ci stepW = m_ScanParam.Size.dwPhyPixels; 980141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) 981141cc406Sopenharmony_ci step = m_ScanParam.Size.dwPhyPixels + 1; 982141cc406Sopenharmony_ci else 983141cc406Sopenharmony_ci step = (m_ScanParam.Size.dwPhyPixels*3) + 1; 984141cc406Sopenharmony_ci 985141cc406Sopenharmony_ci for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ) { 986141cc406Sopenharmony_ci 987141cc406Sopenharmony_ci red = 0; 988141cc406Sopenharmony_ci green = 0; 989141cc406Sopenharmony_ci blue = 0; 990141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) 991141cc406Sopenharmony_ci bufp = ((u_short *)scanbuf)+i; 992141cc406Sopenharmony_ci else 993141cc406Sopenharmony_ci bufp = ((u_short *)scanbuf)+(i*3); 994141cc406Sopenharmony_ci 995141cc406Sopenharmony_ci for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) { 996141cc406Sopenharmony_ci 997141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) { 998141cc406Sopenharmony_ci red += *bufp; bufp+=step; 999141cc406Sopenharmony_ci green += *bufp; bufp+=step; 1000141cc406Sopenharmony_ci blue += *bufp; bufp+=step; 1001141cc406Sopenharmony_ci } else { 1002141cc406Sopenharmony_ci 1003141cc406Sopenharmony_ci red += bufp[0]; 1004141cc406Sopenharmony_ci green += bufp[1]; 1005141cc406Sopenharmony_ci blue += bufp[2]; 1006141cc406Sopenharmony_ci 1007141cc406Sopenharmony_ci bufp += step; 1008141cc406Sopenharmony_ci } 1009141cc406Sopenharmony_ci } 1010141cc406Sopenharmony_ci 1011141cc406Sopenharmony_ci val = ((int)(red/m_ScanParam.Size.dwPhyLines) + param->swOffset[0]); 1012141cc406Sopenharmony_ci if( val < 0 ) { 1013141cc406Sopenharmony_ci DBG( _DBG_INFO, "val < 0!!!!\n" ); 1014141cc406Sopenharmony_ci val = 0; 1015141cc406Sopenharmony_ci } 1016141cc406Sopenharmony_ci a_wDarkShading[i] = (u_short)val; 1017141cc406Sopenharmony_ci 1018141cc406Sopenharmony_ci val = ((int)(green/m_ScanParam.Size.dwPhyLines) + param->swOffset[1]); 1019141cc406Sopenharmony_ci if( val < 0 ) { 1020141cc406Sopenharmony_ci DBG( _DBG_INFO, "val < 0!!!!\n" ); 1021141cc406Sopenharmony_ci val = 0; 1022141cc406Sopenharmony_ci } 1023141cc406Sopenharmony_ci a_wDarkShading[i+stepW] = (u_short)val; 1024141cc406Sopenharmony_ci 1025141cc406Sopenharmony_ci val = ((int)(blue/m_ScanParam.Size.dwPhyLines) + param->swOffset[2]); 1026141cc406Sopenharmony_ci if( val < 0 ) { 1027141cc406Sopenharmony_ci DBG( _DBG_INFO, "val < 0!!!!\n" ); 1028141cc406Sopenharmony_ci val = 0; 1029141cc406Sopenharmony_ci } 1030141cc406Sopenharmony_ci a_wDarkShading[i+stepW*2] = (u_short)val; 1031141cc406Sopenharmony_ci } 1032141cc406Sopenharmony_ci 1033141cc406Sopenharmony_ci } else { 1034141cc406Sopenharmony_ci 1035141cc406Sopenharmony_ci step = m_ScanParam.Size.dwPhyPixels + 1; 1036141cc406Sopenharmony_ci for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ) { 1037141cc406Sopenharmony_ci 1038141cc406Sopenharmony_ci gray = 0; 1039141cc406Sopenharmony_ci bufp = ((u_short *)scanbuf)+i; 1040141cc406Sopenharmony_ci 1041141cc406Sopenharmony_ci for( j=0; j < m_ScanParam.Size.dwPhyLines; j++ ) { 1042141cc406Sopenharmony_ci gray += *bufp; 1043141cc406Sopenharmony_ci bufp += step; 1044141cc406Sopenharmony_ci } 1045141cc406Sopenharmony_ci a_wDarkShading[i]= gray/j + param->swOffset[0]; 1046141cc406Sopenharmony_ci } 1047141cc406Sopenharmony_ci 1048141cc406Sopenharmony_ci memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels, 1049141cc406Sopenharmony_ci a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2); 1050141cc406Sopenharmony_ci memcpy( a_wDarkShading + m_ScanParam.Size.dwPhyPixels * 2, 1051141cc406Sopenharmony_ci a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2); 1052141cc406Sopenharmony_ci } 1053141cc406Sopenharmony_ci 1054141cc406Sopenharmony_ci if(usb_HostSwap()) 1055141cc406Sopenharmony_ci usb_Swap(a_wDarkShading, m_ScanParam.Size.dwPhyPixels * 2 * 3); 1056141cc406Sopenharmony_ci 1057141cc406Sopenharmony_ci usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels, 1058141cc406Sopenharmony_ci scan->sParam.bDataType == SCANDATATYPE_Color?1:0); 1059141cc406Sopenharmony_ci 1060141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustDarkShading() done\n" ); 1061141cc406Sopenharmony_ci return SANE_TRUE; 1062141cc406Sopenharmony_ci} 1063141cc406Sopenharmony_ci 1064141cc406Sopenharmony_ci/** usb_AdjustWhiteShading 1065141cc406Sopenharmony_ci * fine calibration part 2 - read the white calibration area and calculate 1066141cc406Sopenharmony_ci * the gain coefficient for each pixel 1067141cc406Sopenharmony_ci */ 1068141cc406Sopenharmony_cistatic SANE_Bool 1069141cc406Sopenharmony_cicano_AdjustWhiteShading( Plustek_Device *dev, u_short cal_dpi ) 1070141cc406Sopenharmony_ci{ 1071141cc406Sopenharmony_ci char tmp[40]; 1072141cc406Sopenharmony_ci ScanParam *param = &dev->scanning.sParam; 1073141cc406Sopenharmony_ci ScanDef *scan = &dev->scanning; 1074141cc406Sopenharmony_ci u_long *scanbuf = scan->pScanBuffer; 1075141cc406Sopenharmony_ci u_short *bufp; 1076141cc406Sopenharmony_ci unsigned int i, j; 1077141cc406Sopenharmony_ci int step, stepW; 1078141cc406Sopenharmony_ci u_long red, green, blue, gray; 1079141cc406Sopenharmony_ci 1080141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustWhiteShading()\n" ); 1081141cc406Sopenharmony_ci if( usb_IsEscPressed()) 1082141cc406Sopenharmony_ci return SANE_FALSE; 1083141cc406Sopenharmony_ci 1084141cc406Sopenharmony_ci usb_PrepareFineCal( dev, &m_ScanParam, cal_dpi ); 1085141cc406Sopenharmony_ci m_ScanParam.bCalibration = PARAM_WhiteShading; 1086141cc406Sopenharmony_ci 1087141cc406Sopenharmony_ci sprintf( tmp, "fine-white.raw" ); 1088141cc406Sopenharmony_ci DBG( _DBG_INFO2, "FINE WHITE Calibration Strip: %s\n", tmp ); 1089141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Lines = %lu\n", m_ScanParam.Size.dwLines ); 1090141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Pixels = %lu\n", m_ScanParam.Size.dwPixels ); 1091141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Bytes = %lu\n", m_ScanParam.Size.dwBytes ); 1092141cc406Sopenharmony_ci DBG( _DBG_INFO2, "Origin.X = %u\n", m_ScanParam.Origin.x ); 1093141cc406Sopenharmony_ci dumpPicInit(&m_ScanParam, tmp); 1094141cc406Sopenharmony_ci 1095141cc406Sopenharmony_ci if( usb_SetScanParameters( dev, &m_ScanParam ) && 1096141cc406Sopenharmony_ci usb_ScanBegin( dev, SANE_FALSE ) && 1097141cc406Sopenharmony_ci usb_ScanReadImage( dev, scanbuf, m_ScanParam.Size.dwTotalBytes)) { 1098141cc406Sopenharmony_ci 1099141cc406Sopenharmony_ci dumpPic(tmp, (u_char*)scanbuf, m_ScanParam.Size.dwTotalBytes, 0); 1100141cc406Sopenharmony_ci 1101141cc406Sopenharmony_ci if(usb_HostSwap()) 1102141cc406Sopenharmony_ci usb_Swap((u_short *)scanbuf, m_ScanParam.Size.dwTotalBytes); 1103141cc406Sopenharmony_ci 1104141cc406Sopenharmony_ci if (!usb_ScanEnd( dev )) { 1105141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" ); 1106141cc406Sopenharmony_ci return SANE_FALSE; 1107141cc406Sopenharmony_ci } 1108141cc406Sopenharmony_ci } else { 1109141cc406Sopenharmony_ci DBG( _DBG_ERROR, "cano_AdjustWhiteShading() failed\n" ); 1110141cc406Sopenharmony_ci return SANE_FALSE; 1111141cc406Sopenharmony_ci } 1112141cc406Sopenharmony_ci 1113141cc406Sopenharmony_ci /* average the n lines, compute reg values */ 1114141cc406Sopenharmony_ci if( scan->sParam.bDataType == SCANDATATYPE_Color ) { 1115141cc406Sopenharmony_ci 1116141cc406Sopenharmony_ci stepW = m_ScanParam.Size.dwPhyPixels; 1117141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) 1118141cc406Sopenharmony_ci step = m_ScanParam.Size.dwPhyPixels + 1; 1119141cc406Sopenharmony_ci else 1120141cc406Sopenharmony_ci step = (m_ScanParam.Size.dwPhyPixels*3) + 1; 1121141cc406Sopenharmony_ci 1122141cc406Sopenharmony_ci for( i=0; i < m_ScanParam.Size.dwPhyPixels; i++ ) { 1123141cc406Sopenharmony_ci 1124141cc406Sopenharmony_ci red = 0; 1125141cc406Sopenharmony_ci green = 0; 1126141cc406Sopenharmony_ci blue = 0; 1127141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) 1128141cc406Sopenharmony_ci bufp = ((u_short *)scanbuf)+i; 1129141cc406Sopenharmony_ci else 1130141cc406Sopenharmony_ci bufp = ((u_short *)scanbuf)+(i*3); 1131141cc406Sopenharmony_ci 1132141cc406Sopenharmony_ci for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) { 1133141cc406Sopenharmony_ci 1134141cc406Sopenharmony_ci if( usb_IsCISDevice(dev)) { 1135141cc406Sopenharmony_ci red += *bufp; bufp+=step; 1136141cc406Sopenharmony_ci green += *bufp; bufp+=step; 1137141cc406Sopenharmony_ci blue += *bufp; bufp+=step; 1138141cc406Sopenharmony_ci } else { 1139141cc406Sopenharmony_ci red += bufp[0]; 1140141cc406Sopenharmony_ci green += bufp[1]; 1141141cc406Sopenharmony_ci blue += bufp[2]; 1142141cc406Sopenharmony_ci bufp += step; 1143141cc406Sopenharmony_ci } 1144141cc406Sopenharmony_ci } 1145141cc406Sopenharmony_ci 1146141cc406Sopenharmony_ci /* tweaked by the settings in swGain --> 1000/swGain[r,g,b] */ 1147141cc406Sopenharmony_ci red = (65535.*1000./(double)param->swGain[0]) * 16384.*j/red; 1148141cc406Sopenharmony_ci green = (65535.*1000./(double)param->swGain[1]) * 16384.*j/green; 1149141cc406Sopenharmony_ci blue = (65535.*1000./(double)param->swGain[2]) * 16384.*j/blue; 1150141cc406Sopenharmony_ci 1151141cc406Sopenharmony_ci a_wWhiteShading[i] = (red > 65535 ? 65535:red ); 1152141cc406Sopenharmony_ci a_wWhiteShading[i+stepW] = (green > 65535 ? 65535:green); 1153141cc406Sopenharmony_ci a_wWhiteShading[i+stepW*2] = (blue > 65535 ? 65535:blue ); 1154141cc406Sopenharmony_ci } 1155141cc406Sopenharmony_ci 1156141cc406Sopenharmony_ci } else { 1157141cc406Sopenharmony_ci 1158141cc406Sopenharmony_ci step = m_ScanParam.Size.dwPhyPixels + 1; 1159141cc406Sopenharmony_ci for( i=0; i<m_ScanParam.Size.dwPhyPixels; i++ ){ 1160141cc406Sopenharmony_ci gray = 0; 1161141cc406Sopenharmony_ci bufp = ((u_short *)scanbuf)+i; 1162141cc406Sopenharmony_ci 1163141cc406Sopenharmony_ci for( j=0; j<m_ScanParam.Size.dwPhyLines; j++ ) { 1164141cc406Sopenharmony_ci gray += *bufp; 1165141cc406Sopenharmony_ci bufp += step; 1166141cc406Sopenharmony_ci } 1167141cc406Sopenharmony_ci 1168141cc406Sopenharmony_ci gray = (65535.*1000./(double)param->swGain[0]) * 16384.*j/gray; 1169141cc406Sopenharmony_ci 1170141cc406Sopenharmony_ci a_wWhiteShading[i]= (gray > 65535 ? 65535:gray); 1171141cc406Sopenharmony_ci } 1172141cc406Sopenharmony_ci 1173141cc406Sopenharmony_ci memcpy( a_wWhiteShading + m_ScanParam.Size.dwPhyPixels, 1174141cc406Sopenharmony_ci a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2); 1175141cc406Sopenharmony_ci memcpy( a_wWhiteShading + m_ScanParam.Size.dwPhyPixels * 2, 1176141cc406Sopenharmony_ci a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2); 1177141cc406Sopenharmony_ci } 1178141cc406Sopenharmony_ci 1179141cc406Sopenharmony_ci if(usb_HostSwap()) 1180141cc406Sopenharmony_ci usb_Swap(a_wWhiteShading, m_ScanParam.Size.dwPhyPixels * 2 * 3 ); 1181141cc406Sopenharmony_ci 1182141cc406Sopenharmony_ci usb_SaveCalSetShading( dev, &m_ScanParam ); 1183141cc406Sopenharmony_ci 1184141cc406Sopenharmony_ci usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels, 1185141cc406Sopenharmony_ci scan->sParam.bDataType == SCANDATATYPE_Color?1:0); 1186141cc406Sopenharmony_ci 1187141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_AdjustWhiteShading() done\n" ); 1188141cc406Sopenharmony_ci return SANE_TRUE; 1189141cc406Sopenharmony_ci} 1190141cc406Sopenharmony_ci 1191141cc406Sopenharmony_ci/** the entry function for the CIS calibration stuff. 1192141cc406Sopenharmony_ci */ 1193141cc406Sopenharmony_cistatic int 1194141cc406Sopenharmony_cicano_DoCalibration( Plustek_Device *dev ) 1195141cc406Sopenharmony_ci{ 1196141cc406Sopenharmony_ci u_short dpi, idx, idx_end; 1197141cc406Sopenharmony_ci u_long save_waf; 1198141cc406Sopenharmony_ci SANE_Bool skip_fine; 1199141cc406Sopenharmony_ci ScanDef *scan = &dev->scanning; 1200141cc406Sopenharmony_ci HWDef *hw = &dev->usbDev.HwSetting; 1201141cc406Sopenharmony_ci DCapsDef *scaps = &dev->usbDev.Caps; 1202141cc406Sopenharmony_ci 1203141cc406Sopenharmony_ci if( SANE_TRUE == scan->fCalibrated ) 1204141cc406Sopenharmony_ci return SANE_TRUE; 1205141cc406Sopenharmony_ci 1206141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_DoCalibration()\n" ); 1207141cc406Sopenharmony_ci 1208141cc406Sopenharmony_ci if( _IS_PLUSTEKMOTOR(hw->motorModel)){ 1209141cc406Sopenharmony_ci DBG( _DBG_ERROR, "altCalibration can't work with this " 1210141cc406Sopenharmony_ci "Plustek motor control setup\n" ); 1211141cc406Sopenharmony_ci return SANE_FALSE; /* can't cal this */ 1212141cc406Sopenharmony_ci } 1213141cc406Sopenharmony_ci 1214141cc406Sopenharmony_ci /* Don't allow calibration settings from the other driver to confuse our 1215141cc406Sopenharmony_ci * use of a few of its functions. 1216141cc406Sopenharmony_ci */ 1217141cc406Sopenharmony_ci save_waf = scaps->workaroundFlag; 1218141cc406Sopenharmony_ci scaps->workaroundFlag &= ~_WAF_SKIP_WHITEFINE; 1219141cc406Sopenharmony_ci scaps->workaroundFlag &= ~_WAF_SKIP_FINE; 1220141cc406Sopenharmony_ci scaps->workaroundFlag &= ~_WAF_BYPASS_CALIBRATION; 1221141cc406Sopenharmony_ci 1222141cc406Sopenharmony_ci if( !dev->adj.cacheCalData && !usb_IsSheetFedDevice(dev)) 1223141cc406Sopenharmony_ci usb_SpeedTest( dev ); 1224141cc406Sopenharmony_ci 1225141cc406Sopenharmony_ci /* here we handle that warmup stuff for CCD devices */ 1226141cc406Sopenharmony_ci if( !usb_AutoWarmup( dev )) 1227141cc406Sopenharmony_ci return SANE_FALSE; 1228141cc406Sopenharmony_ci 1229141cc406Sopenharmony_ci /* Set the shading position to undefined */ 1230141cc406Sopenharmony_ci strip_state = 0; 1231141cc406Sopenharmony_ci usb_PrepareCalibration( dev ); 1232141cc406Sopenharmony_ci 1233141cc406Sopenharmony_ci usb_SetMCLK( dev, &scan->sParam ); 1234141cc406Sopenharmony_ci 1235141cc406Sopenharmony_ci if( !scan->skipCoarseCalib ) { 1236141cc406Sopenharmony_ci 1237141cc406Sopenharmony_ci if( !usb_Wait4ScanSample( dev )) 1238141cc406Sopenharmony_ci return SANE_FALSE; 1239141cc406Sopenharmony_ci 1240141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### ADJUST LAMP (COARSE)#######\n" ); 1241141cc406Sopenharmony_ci if( cano_PrepareToReadWhiteCal(dev, SANE_TRUE)) 1242141cc406Sopenharmony_ci return SANE_FALSE; 1243141cc406Sopenharmony_ci 1244141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x45] &= ~0x10; 1245141cc406Sopenharmony_ci if( !cano_AdjustLightsource(dev)) { 1246141cc406Sopenharmony_ci DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" ); 1247141cc406Sopenharmony_ci return SANE_FALSE; 1248141cc406Sopenharmony_ci } 1249141cc406Sopenharmony_ci 1250141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### ADJUST OFFSET (COARSE) ####\n" ); 1251141cc406Sopenharmony_ci if(cano_PrepareToReadBlackCal(dev)) 1252141cc406Sopenharmony_ci return SANE_FALSE; 1253141cc406Sopenharmony_ci 1254141cc406Sopenharmony_ci if( !cano_AdjustOffset(dev)) { 1255141cc406Sopenharmony_ci DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" ); 1256141cc406Sopenharmony_ci return SANE_FALSE; 1257141cc406Sopenharmony_ci } 1258141cc406Sopenharmony_ci 1259141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### ADJUST GAIN (COARSE)#######\n" ); 1260141cc406Sopenharmony_ci if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE)) 1261141cc406Sopenharmony_ci return SANE_FALSE; 1262141cc406Sopenharmony_ci 1263141cc406Sopenharmony_ci if( !cano_AdjustGain(dev)) { 1264141cc406Sopenharmony_ci DBG( _DBG_ERROR, "Coarse Calibration failed!!!\n" ); 1265141cc406Sopenharmony_ci return SANE_FALSE; 1266141cc406Sopenharmony_ci } 1267141cc406Sopenharmony_ci } else { 1268141cc406Sopenharmony_ci strip_state = 1; 1269141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### COARSE calibration skipped #######\n" ); 1270141cc406Sopenharmony_ci } 1271141cc406Sopenharmony_ci 1272141cc406Sopenharmony_ci skip_fine = SANE_FALSE; 1273141cc406Sopenharmony_ci idx_end = 2; 1274141cc406Sopenharmony_ci if( dev->adj.cacheCalData || usb_IsSheetFedDevice(dev)) { 1275141cc406Sopenharmony_ci 1276141cc406Sopenharmony_ci skip_fine = usb_FineShadingFromFile(dev); 1277141cc406Sopenharmony_ci 1278141cc406Sopenharmony_ci /* we recalibrate in any case ! */ 1279141cc406Sopenharmony_ci if( usb_InCalibrationMode(dev)) { 1280141cc406Sopenharmony_ci skip_fine = SANE_FALSE; 1281141cc406Sopenharmony_ci idx_end = DIVIDER+1; 1282141cc406Sopenharmony_ci 1283141cc406Sopenharmony_ci /* did I say any case? */ 1284141cc406Sopenharmony_ci if (scan->sParam.bBitDepth != 8) { 1285141cc406Sopenharmony_ci skip_fine = SANE_TRUE; 1286141cc406Sopenharmony_ci DBG( _DBG_INFO2, "No fine calibration for non-8bit modes!\n" ); 1287141cc406Sopenharmony_ci } 1288141cc406Sopenharmony_ci 1289141cc406Sopenharmony_ci } else if( usb_IsSheetFedDevice(dev)) { 1290141cc406Sopenharmony_ci 1291141cc406Sopenharmony_ci /* we only do the calibration upon request !*/ 1292141cc406Sopenharmony_ci if( !skip_fine ) { 1293141cc406Sopenharmony_ci DBG( _DBG_INFO2, "SHEET-FED device, skip fine calibration!\n" ); 1294141cc406Sopenharmony_ci skip_fine = SANE_TRUE; 1295141cc406Sopenharmony_ci scaps->workaroundFlag |= _WAF_BYPASS_CALIBRATION; 1296141cc406Sopenharmony_ci } 1297141cc406Sopenharmony_ci } 1298141cc406Sopenharmony_ci } 1299141cc406Sopenharmony_ci 1300141cc406Sopenharmony_ci if( !skip_fine ) { 1301141cc406Sopenharmony_ci 1302141cc406Sopenharmony_ci for( idx = 1; idx < idx_end; idx++ ) { 1303141cc406Sopenharmony_ci 1304141cc406Sopenharmony_ci dpi = 0; 1305141cc406Sopenharmony_ci if( usb_InCalibrationMode(dev)) { 1306141cc406Sopenharmony_ci dpi = usb_get_res( scaps->OpticDpi.x, idx ); 1307141cc406Sopenharmony_ci 1308141cc406Sopenharmony_ci /* we might should check against device specific limit */ 1309141cc406Sopenharmony_ci if(dpi < 50) 1310141cc406Sopenharmony_ci continue; 1311141cc406Sopenharmony_ci } 1312141cc406Sopenharmony_ci 1313141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### ADJUST DARK (FINE) ########\n" ); 1314141cc406Sopenharmony_ci if(cano_PrepareToReadBlackCal(dev)) 1315141cc406Sopenharmony_ci return SANE_FALSE; 1316141cc406Sopenharmony_ci 1317141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x45] |= 0x10; 1318141cc406Sopenharmony_ci if( !cano_AdjustDarkShading(dev, dpi)) { 1319141cc406Sopenharmony_ci DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" ); 1320141cc406Sopenharmony_ci return SANE_FALSE; 1321141cc406Sopenharmony_ci } 1322141cc406Sopenharmony_ci 1323141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### ADJUST WHITE (FINE) #######\n" ); 1324141cc406Sopenharmony_ci if(cano_PrepareToReadWhiteCal(dev, SANE_FALSE)) 1325141cc406Sopenharmony_ci return SANE_FALSE; 1326141cc406Sopenharmony_ci 1327141cc406Sopenharmony_ci if( !usb_IsSheetFedDevice(dev)) { 1328141cc406Sopenharmony_ci if(!usb_ModuleToHome( dev, SANE_TRUE )) 1329141cc406Sopenharmony_ci return SANE_FALSE; 1330141cc406Sopenharmony_ci 1331141cc406Sopenharmony_ci if( !usb_ModuleMove(dev, MOVE_Forward, 1332141cc406Sopenharmony_ci (u_long)dev->usbDev.pSource->ShadingOriginY)) { 1333141cc406Sopenharmony_ci return SANE_FALSE; 1334141cc406Sopenharmony_ci } 1335141cc406Sopenharmony_ci } 1336141cc406Sopenharmony_ci if( !cano_AdjustWhiteShading(dev, dpi)) { 1337141cc406Sopenharmony_ci DBG( _DBG_ERROR, "Fine Calibration failed!!!\n" ); 1338141cc406Sopenharmony_ci return SANE_FALSE; 1339141cc406Sopenharmony_ci } 1340141cc406Sopenharmony_ci 1341141cc406Sopenharmony_ci /* force to go back */ 1342141cc406Sopenharmony_ci strip_state = 0; 1343141cc406Sopenharmony_ci } 1344141cc406Sopenharmony_ci } else { 1345141cc406Sopenharmony_ci DBG( _DBG_INFO2, "###### FINE calibration skipped #######\n" ); 1346141cc406Sopenharmony_ci 1347141cc406Sopenharmony_ci dev->usbDev.a_bRegs[0x45] |= 0x10; 1348141cc406Sopenharmony_ci strip_state = 2; 1349141cc406Sopenharmony_ci 1350141cc406Sopenharmony_ci m_ScanParam = scan->sParam; 1351141cc406Sopenharmony_ci usb_GetPhyPixels( dev, &m_ScanParam ); 1352141cc406Sopenharmony_ci 1353141cc406Sopenharmony_ci usb_line_statistics( "Dark", a_wDarkShading, m_ScanParam.Size.dwPhyPixels, 1354141cc406Sopenharmony_ci m_ScanParam.bDataType == SCANDATATYPE_Color?1:0); 1355141cc406Sopenharmony_ci usb_line_statistics( "White", a_wWhiteShading, m_ScanParam.Size.dwPhyPixels, 1356141cc406Sopenharmony_ci m_ScanParam.bDataType == SCANDATATYPE_Color?1:0); 1357141cc406Sopenharmony_ci } 1358141cc406Sopenharmony_ci 1359141cc406Sopenharmony_ci /* Lamp on if it's not */ 1360141cc406Sopenharmony_ci cano_LampOnAfterCalibration(dev); 1361141cc406Sopenharmony_ci strip_state = 0; 1362141cc406Sopenharmony_ci 1363141cc406Sopenharmony_ci /* home the sensor after calibration 1364141cc406Sopenharmony_ci */ 1365141cc406Sopenharmony_ci if( !usb_IsSheetFedDevice(dev)) 1366141cc406Sopenharmony_ci usb_ModuleToHome( dev, SANE_TRUE ); 1367141cc406Sopenharmony_ci scan->fCalibrated = SANE_TRUE; 1368141cc406Sopenharmony_ci 1369141cc406Sopenharmony_ci DBG( _DBG_INFO, "cano_DoCalibration() done\n" ); 1370141cc406Sopenharmony_ci DBG( _DBG_INFO, "-------------------------\n" ); 1371141cc406Sopenharmony_ci DBG( _DBG_INFO, "Static Gain:\n" ); 1372141cc406Sopenharmony_ci DBG( _DBG_INFO, "REG[0x3b] = %u\n", dev->usbDev.a_bRegs[0x3b] ); 1373141cc406Sopenharmony_ci DBG( _DBG_INFO, "REG[0x3c] = %u\n", dev->usbDev.a_bRegs[0x3c] ); 1374141cc406Sopenharmony_ci DBG( _DBG_INFO, "REG[0x3d] = %u\n", dev->usbDev.a_bRegs[0x3d] ); 1375141cc406Sopenharmony_ci DBG( _DBG_INFO, "Static Offset:\n" ); 1376141cc406Sopenharmony_ci DBG( _DBG_INFO, "REG[0x38] = %i\n", dev->usbDev.a_bRegs[0x38] ); 1377141cc406Sopenharmony_ci DBG( _DBG_INFO, "REG[0x39] = %i\n", dev->usbDev.a_bRegs[0x39] ); 1378141cc406Sopenharmony_ci DBG( _DBG_INFO, "REG[0x3a] = %i\n", dev->usbDev.a_bRegs[0x3a] ); 1379141cc406Sopenharmony_ci DBG( _DBG_INFO, "-------------------------\n" ); 1380141cc406Sopenharmony_ci 1381141cc406Sopenharmony_ci scaps->workaroundFlag |= save_waf; 1382141cc406Sopenharmony_ci 1383141cc406Sopenharmony_ci return SANE_TRUE; 1384141cc406Sopenharmony_ci} 1385141cc406Sopenharmony_ci 1386141cc406Sopenharmony_ci/* END PLUSTEK-USBCAL.C .....................................................*/ 1387