/* @file plustek-pp_detect.c * @brief automatic scanner detection * * based on sources acquired from Plustek Inc. * Copyright (C) 1998 Plustek Inc. * Copyright (C) 2000-2013 Gerhard Jaeger * also based on the work done by Rick Bronson * * History: * - 0.30 - initial version * - 0.31 - no changes * - 0.32 - no changes * - 0.33 - added portmode check * - 0.34 - no changes * - 0.35 - no changes * - 0.36 - added some debug messages * - replace the old _OUTB/_INB macros * - 0.37 - cosmetic changes * - added speed-test for the parallel-port * - 0.38 - added P12 stuff - replaced detectP9636 by detectAsic9800x * - added detectResetPort() function * - 0.39 - fixed problem in ASIC9800x detection * - 0.40 - no changes * - 0.41 - no changes * - 0.42 - changed include names * - 0.43 - cleanup * - 0.44 - fix format string issues, as Long types default to int32_t * now * . *
* 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, see . * * 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. *
*/ #include "plustek-pp_scan.h" /************************** local definitions ********************************/ /*************************** local functions *********************************/ /** as the name says... */ static void detectResetPort( pScanData ps ) { UChar control; DBG( DBG_HIGH, "ResetPort()\n" ); control = _INB_CTRL( ps ); _DO_UDELAY( 2 ); _OUTB_CTRL( ps, _CTRL_RESERVED ); /* reset, 0xc0 */ _DO_UDELAY( 2 ); _OUTB_CTRL( ps, control ); /* and restore... */ _DO_UDELAY( 2 ); } /** Check: will the status port changed between printer/scanner path changed? * Write out data and read in to compare */ static int detectScannerConnection( pScanData ps ) { UChar data, control, status; int retval = _E_NO_CONN; detectResetPort( ps ); /* * as we're called during InitPorts, we can be sure * to operate in EPP-mode (hopefully ;-) */ control = _INB_CTRL( ps ); /* * go ahead and do some checks */ _OUTB_CTRL( ps, _CTRL_GENSIGNAL ); _DO_UDELAY( 5 ); _OUTB_DATA( ps, 0x55 ); _DO_UDELAY( 5 ); data = _INB_DATA( ps ); if (0x55 == data) { DBG( DBG_HIGH, "Test 0x55\n" ); _OUTB_DATA( ps, 0xAA ); _DO_UDELAY( 5 ); data = _INB_DATA( ps ); if (0xAA == data) { DBG( DBG_HIGH, "Test 0xAA\n" ); _OUTB_DATA( ps, 0x0 ); _DO_UDELAY( 5 ); data = _INB_STATUS( ps ); ps->OpenScanPath( ps ); _OUTB_DATA( ps, 0x0 ); _DO_UDELAY( 5 ); status = _INB_STATUS( ps ); ps->CloseScanPath( ps ); /* * so we're done 'til now... */ DBG( DBG_HIGH, "Compare data=0x%x and status=0x%x, port=0x%x\n", data, status, ps->IO.portBase ); if( data != status ) { _ASSERT( ps->ReadWriteTest ); /* * here we try to detect the operation speed of our parallel * port if we have tested all the stuff and had no success, * retval will contain the error-code */ for( ps->IO.delay = 0; ps->IO.delay < 5; ps->IO.delay++ ) { retval = ps->ReadWriteTest( ps ); /* break on OK or when the ASIC detection fails */ if((_OK == retval) || (_E_NO_ASIC == retval)) break; } } } } /* work on the result */ if ( _OK == retval ) { ps->sCaps.wIOBase = ps->pardev; ps->PutToIdleMode( ps ); } else { ps->sCaps.wIOBase = _NO_BASE; } /* * restore control port value */ _OUTB_CTRL( ps, control ); _DO_UDELAY( 5 ); DBG( DBG_HIGH, "detectScannerConnection() returns %i.\n", retval ); return retval; } /** we need some memory... */ static int detectSetupBuffers( pScanData ps ) { DBG( DBG_LOW, "*** setupBuffers ***\n" ); /* bad news ? */ if ( 0 == ps->TotalBufferRequire ) { DBG( DBG_HIGH, "pt_drv: asic 0x%x probably not supported\n", ps->sCaps.AsicID); return _E_ALLOC; /* Out of memory */ } else { /* * allocate and clear */ DBG(DBG_LOW,"Driverbuf(%u bytes) needed !\n", ps->TotalBufferRequire); ps->driverbuf = (pUChar)_VMALLOC(ps->TotalBufferRequire); if ( NULL == ps->driverbuf ) { DBG( DBG_HIGH, "pt_drv: Not enough kernel memory %d\n", ps->TotalBufferRequire); return _E_ALLOC; /* Out of memory */ } memset( ps->driverbuf, 0, ps->TotalBufferRequire ); } ps->pPrescan16 = ps->driverbuf; ps->pPrescan8 = ps->pPrescan16 + ps->BufferFor1stColor; ps->pScanBuffer1 = ps->pPrescan8 + ps->BufferFor2ndColor; /* CHECK: Should we adjust that !!! */ ps->pEndBufR = ps->pPrescan8; ps->pEndBufG = ps->pScanBuffer1; ps->pColorRunTable = ps->pScanBuffer1 + ps->BufferForDataRead1; DBG( DBG_LOW, "pColorRunTab = 0x%0lx - 0x%0lx\n", (unsigned long)ps->pColorRunTable, (unsigned long)((pUChar)ps->driverbuf + ps->TotalBufferRequire)); if ( _ASIC_IS_98001 == ps->sCaps.AsicID ) { DBG( DBG_LOW, "Adjust for 98001 ASIC\n" ); ps->pScanBuffer2 = ps->pPrescan16; ps->pScanBuffer1 = ps->pScanBuffer2 + _LINE_BUFSIZE1; ps->pColorRunTable = ps->pScanBuffer1 + _LINE_BUFSIZE * 2UL; ps->pProcessingBuf = ps->pColorRunTable + ps->BufferForColorRunTable; DBG( DBG_LOW, "sb2 = 0x%lx, sb1 = 0x%lx, Color = 0x%lx\n", (unsigned long)ps->pScanBuffer2, (unsigned long)ps->pScanBuffer1, (unsigned long)ps->pColorRunTable ); DBG( DBG_LOW, "Pro = 0x%lx, size = %d\n", (unsigned long)ps->pProcessingBuf, ps->TotalBufferRequire ); ps->dwShadow = (_DEF_BRIGHTEST_SKIP + _DEF_DARKEST_SKIP) * 5400UL * 2UL * 3UL; ps->Shade.pHilight = _VMALLOC( ps->dwShadow ); if ( NULL != ps->Shade.pHilight ) { memset( ps->Shade.pHilight, 0, ps->dwShadow ); ps->dwHilight = _DEF_BRIGHTEST_SKIP * 5400UL * 3UL; ps->dwShadow = _DEF_DARKEST_SKIP * 5400UL * 3UL; ps->pwShadow = (pUShort)ps->Shade.pHilight + ps->dwHilight; ps->Shade.dwDiv = 32UL - _DEF_BRIGHTEST_SKIP - _DEF_DARKEST_SKIP; ps->dwHilightCh = ps->dwHilight / 3UL; ps->dwShadowCh = ps->dwShadow / 3UL; } } else if ( _ASIC_IS_98003 == ps->sCaps.AsicID ) { DBG( DBG_LOW, "Adjust for 98003 ASIC\n" ); ps->Bufs.b1.pReadBuf = ps->driverbuf; ps->Bufs.b2.pSumBuf = ps->Bufs.b1.pReadBuf + _SizeDataBuf; ps->Bufs.TpaBuf.pb = &((pUChar)ps->Bufs.b2.pSumBuf)[_SizeShadingSumBuf]; /* CHECK: We might should play around with these values... */ ps->Shade.skipHilight = _DEF_BRIGHTEST_SKIP; ps->Shade.skipShadow = _DEF_DARKEST_SKIP; if( ps->Shade.skipHilight && ps->Shade.skipShadow ) { ULong skipSize; skipSize = (ULong)((ps->Shade.skipHilight + ps->Shade.skipShadow) * _SizeDataBuf * 3); ps->Shade.pHilight = _VMALLOC( skipSize ); if( NULL != ps->Shade.pHilight ) { ps->Shade.dwDiv = (ULong)(32UL - ps->Shade.skipHilight - ps->Shade.skipShadow); } } else ps->Shade.pHilight = NULL; } return _OK; } /** model 48xx detection or any other model using the 96001/3 ASIC */ static int detectP48xx( pScanData ps ) { int result; DBG( DBG_LOW, "************ DETECTP48xx ************\n" ); /* increase the delay-time */ ps->IO.delay = 4; ModelSet4800( ps ); result = P48xxInitAsic( ps ); if( _OK != result ) return result; return detectScannerConnection( ps ); } /** ASIC 98003 model detection */ static int detectAsic98003( pScanData ps ) { int result; DBG( DBG_LOW, "************* ASIC98003 *************\n" ); /* increase the delay-time */ ps->IO.delay = 4; ModelSetP12( ps ); result = P12InitAsic( ps ); if( _OK != result ) return result; IOSoftwareReset( ps ); return detectScannerConnection( ps ); } /** ASIC 98001 model detection */ static int detectAsic98001( pScanData ps ) { int result; DBG( DBG_LOW, "************* ASIC98001 *************\n" ); /* increase the delay-time */ ps->IO.delay = 4; ModelSet9636( ps ); result = P9636InitAsic( ps ); #ifndef _ASIC_98001_SIM if( _OK != result ) return result; return detectScannerConnection( ps ); #else DBG( DBG_HIGH, "!!!! WARNING, have a look at function detectAsic98001() !!!!\n" ); ps->sCaps.AsicID = _ASIC_IS_98001; ps->sCaps.wIOBase = ps->IO.pbSppDataPort; return _OK; #endif } /************************ exported functions *********************************/ /** here we try to find the scanner, depending on the mode */ _LOC int DetectScanner( pScanData ps, int mode ) { Byte asic; int result = _E_INTERNAL; /* * before doing anything else, check the port-mode */ if((ps->IO.portMode != _PORT_EPP) && (ps->IO.portMode != _PORT_SPP) && (ps->IO.portMode != _PORT_BIDI)) { DBG( DBG_LOW, "!!! Portmode (%u)not supported !!!\n", ps->IO.portMode ); return _E_INTERNAL; } /* autodetection ? */ if( 0 == mode ) { DBG( DBG_HIGH, "Starting Scanner-Autodetection\n" ); /* try to find a 48xx Scanner * (or even a scanner based on the 96001/3) ASIC */ result = detectP48xx( ps ); if( _OK != result ) { DBG( DBG_LOW, "************* ASIC9800x *************\n" ); /* get the ASIC ID by using the OpenScanPath stuff from Asic9600x based * models - only difference: change the ReadHigh/ReadLow signals before */ ps->CtrlReadHighNibble = _CTRL_GENSIGNAL+_CTRL_AUTOLF+_CTRL_STROBE; ps->CtrlReadLowNibble = _CTRL_GENSIGNAL+_CTRL_AUTOLF; /* read Register 0x18 (AsicID Register) of Asic9800x based devices */ #ifdef _ASIC_98001_SIM DBG( DBG_HIGH, "!!!! WARNING, SW-Emulation active !!!!\n" ); asic = _ASIC_IS_98001; #else detectResetPort( ps ); /* do some presettings to make IODataRegisterFromScanner() work */ ps->RegAsicID = 0x18; ps->IO.useEPPCmdMode = _FALSE; ps->sCaps.AsicID = _ASIC_IS_98001; IOInitialize( ps ); asic = IODataRegisterFromScanner( ps, ps->RegAsicID ); DBG( DBG_HIGH, "ASIC = 0x%02X\n", asic ); #endif /* depending on what we have found, perform some extra tests */ switch( asic ) { case _ASIC_IS_98001: result = detectAsic98001( ps ); break; case _ASIC_IS_98003: /* as the reading of the ASIC ID causes trouble, * we reset the device */ ps->IO.useEPPCmdMode = _FALSE; ps->sCaps.AsicID = _ASIC_IS_98003; IOInitialize( ps ); IOSoftwareReset( ps ); result = detectAsic98003( ps ); break; default: DBG( DBG_HIGH, "Unknown ASIC-ID\n" ); result = _E_NO_DEV; break; } } } else { /* this will be called each time before operating on a previously * detected device, to make sure we are still operating on the same one */ if( _ASIC_IS_98001 == mode ) { DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 98001)\n" ); result = detectAsic98001( ps ); } else if( _ASIC_IS_98003 == mode ) { DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 98003)\n" ); result = detectAsic98003( ps ); } else { DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 96001/3)\n" ); result = detectP48xx( ps ); } } if( _OK == result ) { _ASSERT( ps->SetupScannerVariables ); ps->SetupScannerVariables( ps ); detectSetupBuffers( ps ); } else { /* CHECK - we should not need that anymore - paranoia code ??!!!! */ ps->sCaps.wIOBase = _NO_BASE; } DBG( DBG_LOW, "*** DETECTION DONE, result: %i ***\n", result ); return result; } /* END PLUSTEK-PP_DETECT.C ..................................................*/