/*............................................................................. * Project : linux driver for Plustek parallel-port scanners *............................................................................. * File: plustek-pp_detect.c - automatic scanner detection *............................................................................. * * based on sources acquired from Plustek Inc. * Copyright (C) 1998 Plustek Inc. * Copyright (C) 2000-2003 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 * *............................................................................. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "plustek-pp_scan.h" /************************** local definitions ********************************/ /*************************** local functions *********************************/ /*............................................................................. * as the name says... */ static void detectResetPort( pScanData ps ) { UChar control; 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; DBG( DBG_LOW, "Dataport = 0x%04x\n", ps->IO.pbSppDataPort ); DBG( DBG_LOW, "Ctrlport = 0x%04x\n", ps->IO.pbControlPort ); detectResetPort( ps ); /* * as we´re called during InitPorts, we can be sure * to operate in EPP-mode (hopefuly ;-) */ 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->IO.pbSppDataPort; 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 ) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "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(%lu bytes) needed !\n", ps->TotalBufferRequire); ps->driverbuf = (pUChar)_VMALLOC(ps->TotalBufferRequire); if ( NULL == ps->driverbuf ) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv: Not enough kernel memory %ld\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", (ULong)ps->pColorRunTable, (ULong)((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", (ULong)ps->pScanBuffer2, (ULong)ps->pScanBuffer1, (ULong)ps->pColorRunTable ); DBG( DBG_LOW, "Pro = 0x%lx, size = %ld\n", (ULong)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 #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "!!!! 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 */ 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 #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "!!!! 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're already 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 ..................................................*/