1 /* @file plustek-pp_detect.c
2  * @brief automatic scanner detection
3  *
4  * based on sources acquired from Plustek Inc.
5  * Copyright (C) 1998 Plustek Inc.
6  * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
7  * also based on the work done by Rick Bronson
8  *
9  * History:
10  * - 0.30 - initial version
11  * - 0.31 - no changes
12  * - 0.32 - no changes
13  * - 0.33 - added portmode check
14  * - 0.34 - no changes
15  * - 0.35 - no changes
16  * - 0.36 - added some debug messages
17  *        - replace the old _OUTB/_INB macros
18  * - 0.37 - cosmetic changes
19  *        - added speed-test for the parallel-port
20  * - 0.38 - added P12 stuff - replaced detectP9636 by detectAsic9800x
21  *        - added detectResetPort() function
22  * - 0.39 - fixed problem in ASIC9800x detection
23  * - 0.40 - no changes
24  * - 0.41 - no changes
25  * - 0.42 - changed include names
26  * - 0.43 - cleanup
27  * - 0.44 - fix format string issues, as Long types default to int32_t
28  *          now
29  * .
30  * <hr>
31  * This file is part of the SANE package.
32  *
33  * This program is free software; you can redistribute it and/or
34  * modify it under the terms of the GNU General Public License as
35  * published by the Free Software Foundation; either version 2 of the
36  * License, or (at your option) any later version.
37  *
38  * This program is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
45  *
46  * As a special exception, the authors of SANE give permission for
47  * additional uses of the libraries contained in this release of SANE.
48  *
49  * The exception is that, if you link a SANE library with other files
50  * to produce an executable, this does not by itself cause the
51  * resulting executable to be covered by the GNU General Public
52  * License.  Your use of that executable is in no way restricted on
53  * account of linking the SANE library code into it.
54  *
55  * This exception does not, however, invalidate any other reasons why
56  * the executable file might be covered by the GNU General Public
57  * License.
58  *
59  * If you submit changes to SANE to the maintainers to be included in
60  * a subsequent release, you agree by submitting the changes that
61  * those changes may be distributed with this exception intact.
62  *
63  * If you write modifications of your own for SANE, it is your choice
64  * whether to permit this exception to apply to your modifications.
65  * If you do not wish that, delete this exception notice.
66  * <hr>
67  */
68 #include "plustek-pp_scan.h"
69 
70 /************************** local definitions ********************************/
71 
72 /*************************** local functions *********************************/
73 
74 /** as the name says...
75  */
detectResetPort( pScanData ps )76 static void detectResetPort( pScanData ps )
77 {
78 	UChar control;
79 
80 	DBG( DBG_HIGH, "ResetPort()\n" );
81 
82 	control = _INB_CTRL( ps );
83 	_DO_UDELAY( 2 );
84 
85 	_OUTB_CTRL( ps, _CTRL_RESERVED );   /* reset, 0xc0      */
86 	_DO_UDELAY( 2 );
87 
88 	_OUTB_CTRL( ps, control );          /* and restore...   */
89 	_DO_UDELAY( 2 );
90 }
91 
92 /** Check: will the status port changed between printer/scanner path changed?
93  *  Write out data and read in to compare
94  */
detectScannerConnection( pScanData ps )95 static int detectScannerConnection( pScanData ps )
96 {
97 	UChar data, control, status;
98 	int   retval = _E_NO_CONN;
99 
100 	detectResetPort( ps );
101 
102 	/*
103 	 * as we're called during InitPorts, we can be sure
104 	 * to operate in EPP-mode (hopefully ;-)
105 	 */
106 	control = _INB_CTRL( ps );
107 
108 	/*
109 	 * go ahead and do some checks
110 	 */
111 	_OUTB_CTRL( ps, _CTRL_GENSIGNAL );
112 	_DO_UDELAY( 5 );
113 
114 	_OUTB_DATA( ps, 0x55 );
115 	_DO_UDELAY( 5 );
116 
117 	data = _INB_DATA( ps );
118 
119 	if (0x55 == data) {
120 
121 		DBG( DBG_HIGH, "Test 0x55\n" );
122 
123     	_OUTB_DATA( ps, 0xAA );
124 		_DO_UDELAY( 5 );
125 
126 	   	data = _INB_DATA( ps );
127 
128     	if (0xAA == data) {
129 
130 			DBG( DBG_HIGH, "Test 0xAA\n" );
131 
132 			_OUTB_DATA( ps, 0x0 );
133 			_DO_UDELAY( 5 );
134 
135 			data = _INB_STATUS( ps );
136 
137 			ps->OpenScanPath( ps );
138 
139 			_OUTB_DATA( ps, 0x0 );
140 			_DO_UDELAY( 5 );
141 
142 			status = _INB_STATUS( ps );
143 
144 			ps->CloseScanPath( ps );
145 
146 			/*
147 	 		 * so we're done 'til now...
148 			 */
149 			DBG( DBG_HIGH, "Compare data=0x%x and status=0x%x, port=0x%x\n",
150 		  				  data, status, ps->IO.portBase );
151 
152 			if( data != status ) {
153 
154 				_ASSERT( ps->ReadWriteTest );
155 
156 				/*
157 				 * here we try to detect the operation speed of our parallel
158 				 * port if we have tested all the stuff and had no success,
159 				 * retval will contain the error-code
160                  */
161 				for( ps->IO.delay = 0; ps->IO.delay < 5; ps->IO.delay++ ) {
162 
163 					retval = ps->ReadWriteTest( ps );
164 
165 					/* break on OK or when the ASIC detection fails */
166 					if((_OK == retval) ||  (_E_NO_ASIC == retval))
167 						break;
168 				}
169 			}
170 		}
171 	}
172 
173 	/* work on the result */
174 	if ( _OK == retval ) {
175 		ps->sCaps.wIOBase = ps->pardev;
176 		ps->PutToIdleMode( ps );
177 
178 	} else {
179     	ps->sCaps.wIOBase = _NO_BASE;
180 	}
181 
182 	/*
183 	 * restore control port value
184 	 */
185 	_OUTB_CTRL( ps, control );
186 	_DO_UDELAY( 5 );
187 
188 	DBG( DBG_HIGH, "detectScannerConnection() returns %i.\n", retval );
189 
190 	return retval;
191 }
192 
193 /** we need some memory...
194  */
detectSetupBuffers( pScanData ps )195 static int detectSetupBuffers( pScanData ps )
196 {
197 	DBG( DBG_LOW, "*** setupBuffers ***\n" );
198 
199     /* bad news ?
200      */
201     if ( 0 == ps->TotalBufferRequire ) {
202 
203 		DBG( DBG_HIGH,
204         "pt_drv: asic 0x%x probably not supported\n", ps->sCaps.AsicID);
205 
206         return _E_ALLOC;  /* Out of memory */
207 
208     } else {
209 
210 		/*
211 		 * allocate and clear
212 		 */
213 		DBG(DBG_LOW,"Driverbuf(%u bytes) needed !\n", ps->TotalBufferRequire);
214         ps->driverbuf = (pUChar)_VMALLOC(ps->TotalBufferRequire);
215 
216         if ( NULL == ps->driverbuf ) {
217 
218 		DBG( DBG_HIGH,
219              "pt_drv: Not enough kernel memory %d\n",
220                     ps->TotalBufferRequire);
221             return _E_ALLOC;  /* Out of memory */
222         }
223 
224 		memset( ps->driverbuf, 0, ps->TotalBufferRequire );
225     }
226 
227     ps->pPrescan16   = ps->driverbuf;
228     ps->pPrescan8    = ps->pPrescan16 + ps->BufferFor1stColor;
229     ps->pScanBuffer1 = ps->pPrescan8  + ps->BufferFor2ndColor;
230 
231 /* CHECK: Should we adjust that !!!
232 */
233     ps->pEndBufR       = ps->pPrescan8;
234     ps->pEndBufG       = ps->pScanBuffer1;
235     ps->pColorRunTable = ps->pScanBuffer1 + ps->BufferForDataRead1;
236 
237 	DBG( DBG_LOW, "pColorRunTab = 0x%0lx - 0x%0lx\n",
238 			(unsigned long)ps->pColorRunTable,
239 			(unsigned long)((pUChar)ps->driverbuf + ps->TotalBufferRequire));
240 
241     if ( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
242 
243 		DBG( DBG_LOW, "Adjust for 98001 ASIC\n" );
244 
245         ps->pScanBuffer2   = ps->pPrescan16;
246         ps->pScanBuffer1   = ps->pScanBuffer2 + _LINE_BUFSIZE1;
247         ps->pColorRunTable = ps->pScanBuffer1 + _LINE_BUFSIZE * 2UL;
248         ps->pProcessingBuf = ps->pColorRunTable + ps->BufferForColorRunTable;
249         DBG( DBG_LOW, "sb2 = 0x%lx, sb1 = 0x%lx, Color = 0x%lx\n",
250 					(unsigned long)ps->pScanBuffer2,
251 					(unsigned long)ps->pScanBuffer1,
252 					(unsigned long)ps->pColorRunTable );
253         DBG( DBG_LOW, "Pro = 0x%lx, size = %d\n",
254 					(unsigned long)ps->pProcessingBuf, ps->TotalBufferRequire );
255 
256         ps->dwShadow = (_DEF_BRIGHTEST_SKIP + _DEF_DARKEST_SKIP) * 5400UL * 2UL * 3UL;
257 
258         ps->Shade.pHilight = _VMALLOC( ps->dwShadow );
259 
260         if ( NULL != ps->Shade.pHilight ) {
261 
262 			memset( ps->Shade.pHilight, 0, ps->dwShadow );
263 
264             ps->dwHilight   = _DEF_BRIGHTEST_SKIP * 5400UL * 3UL;
265             ps->dwShadow    = _DEF_DARKEST_SKIP   * 5400UL * 3UL;
266             ps->pwShadow    = (pUShort)ps->Shade.pHilight + ps->dwHilight;
267             ps->Shade.dwDiv = 32UL - _DEF_BRIGHTEST_SKIP - _DEF_DARKEST_SKIP;
268 
269             ps->dwHilightCh = ps->dwHilight / 3UL;
270             ps->dwShadowCh  = ps->dwShadow  / 3UL;
271         }
272     } else if ( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
273 
274 		DBG( DBG_LOW, "Adjust for 98003 ASIC\n" );
275 
276         ps->Bufs.b1.pReadBuf = ps->driverbuf;
277     	ps->Bufs.b2.pSumBuf  = ps->Bufs.b1.pReadBuf + _SizeDataBuf;
278         ps->Bufs.TpaBuf.pb   = &((pUChar)ps->Bufs.b2.pSumBuf)[_SizeShadingSumBuf];
279 
280 /* CHECK: We might should play around with these values... */
281         ps->Shade.skipHilight = _DEF_BRIGHTEST_SKIP;
282         ps->Shade.skipShadow  = _DEF_DARKEST_SKIP;
283 
284     	if( ps->Shade.skipHilight && ps->Shade.skipShadow ) {
285 
286             ULong skipSize;
287 
288 	        skipSize = (ULong)((ps->Shade.skipHilight + ps->Shade.skipShadow)
289                                                            * _SizeDataBuf * 3);
290 
291     	    ps->Shade.pHilight = _VMALLOC( skipSize );
292 
293             if( NULL != ps->Shade.pHilight ) {
294     	        ps->Shade.dwDiv = (ULong)(32UL - ps->Shade.skipHilight -
295                                                         ps->Shade.skipShadow);
296             }
297         } else
298     	    ps->Shade.pHilight = NULL;
299     }
300 
301     return _OK;
302 }
303 
304 /** model 48xx detection or any other model using the 96001/3 ASIC
305  */
detectP48xx( pScanData ps )306 static int detectP48xx( pScanData ps )
307 {
308 	int result;
309 
310 	DBG( DBG_LOW, "************ DETECTP48xx ************\n" );
311 
312 	/* increase the delay-time */
313 	ps->IO.delay = 4;
314 
315 	ModelSet4800( ps );
316 
317 	result = P48xxInitAsic( ps );
318 	if( _OK != result )
319 		return result;
320 
321 	return detectScannerConnection( ps );
322 }
323 
324 /** ASIC 98003 model detection
325  */
detectAsic98003( pScanData ps )326 static int detectAsic98003( pScanData ps )
327 {
328 	int  result;
329 
330 	DBG( DBG_LOW, "************* ASIC98003 *************\n" );
331 
332 	/* increase the delay-time */
333 	ps->IO.delay = 4;
334 
335    	ModelSetP12( ps );
336 
337     result = P12InitAsic( ps );
338 	if( _OK != result )
339 		return result;
340 
341     IOSoftwareReset( ps );
342 
343 	return detectScannerConnection( ps );
344 }
345 
346 /** ASIC 98001 model detection
347  */
detectAsic98001( pScanData ps )348 static int detectAsic98001( pScanData ps )
349 {
350 	int  result;
351 
352 	DBG( DBG_LOW, "************* ASIC98001 *************\n" );
353 
354 	/* increase the delay-time */
355 	ps->IO.delay = 4;
356 
357     ModelSet9636( ps );
358 
359    	result = P9636InitAsic( ps );
360 #ifndef _ASIC_98001_SIM
361 	if( _OK != result )
362 		return result;
363 
364 	return detectScannerConnection( ps );
365 #else
366 		DBG( DBG_HIGH,
367 			"!!!! WARNING, have a look at function detectAsic98001() !!!!\n" );
368    	ps->sCaps.AsicID  =  _ASIC_IS_98001;
369   	ps->sCaps.wIOBase = ps->IO.pbSppDataPort;
370     return _OK;
371 #endif
372 }
373 
374 /************************ exported functions *********************************/
375 
376 /** here we try to find the scanner, depending on the mode
377  */
DetectScanner( pScanData ps, int mode )378 _LOC int DetectScanner( pScanData ps, int mode )
379 {
380     Byte asic;
381 	int  result = _E_INTERNAL;
382 
383 	/*
384 	 * before doing anything else, check the port-mode
385 	 */
386 	if((ps->IO.portMode != _PORT_EPP) && (ps->IO.portMode != _PORT_SPP) &&
387 	   (ps->IO.portMode != _PORT_BIDI)) {
388 
389 		DBG( DBG_LOW, "!!! Portmode (%u)not supported !!!\n", ps->IO.portMode );
390 		return _E_INTERNAL;
391 	}
392 
393 	/* autodetection ?
394 	 */
395 	if( 0 == mode ) {
396 
397 		DBG( DBG_HIGH, "Starting Scanner-Autodetection\n" );
398 
399 		/* try to find a 48xx Scanner
400 		 * (or even a scanner based on the 96001/3) ASIC
401 		 */
402 		result = detectP48xx( ps );
403 
404 		if( _OK != result ) {
405 
406         	DBG( DBG_LOW, "************* ASIC9800x *************\n" );
407 
408             /* get the ASIC ID by using the OpenScanPath stuff from Asic9600x based
409              * models - only difference: change the ReadHigh/ReadLow signals before
410              */
411         	ps->CtrlReadHighNibble = _CTRL_GENSIGNAL+_CTRL_AUTOLF+_CTRL_STROBE;
412             ps->CtrlReadLowNibble  = _CTRL_GENSIGNAL+_CTRL_AUTOLF;
413 
414             /* read Register 0x18 (AsicID Register) of Asic9800x based devices */
415 #ifdef _ASIC_98001_SIM
416 			DBG( DBG_HIGH,
417 						"!!!! WARNING, SW-Emulation active !!!!\n" );
418             asic = _ASIC_IS_98001;
419 #else
420             detectResetPort( ps );
421 
422             /* do some presettings to make IODataRegisterFromScanner() work */
423             ps->RegAsicID        = 0x18;
424             ps->IO.useEPPCmdMode = _FALSE;
425             ps->sCaps.AsicID     = _ASIC_IS_98001;
426             IOInitialize( ps );
427 
428             asic = IODataRegisterFromScanner( ps, ps->RegAsicID );
429 
430             DBG( DBG_HIGH, "ASIC = 0x%02X\n", asic  );
431 #endif
432 
433             /* depending on what we have found, perform some extra tests */
434    	        switch( asic ) {
435 
436        	    case _ASIC_IS_98001:
437            	    result = detectAsic98001( ps );
438                	break;
439 
440             case _ASIC_IS_98003:
441 
442                 /* as the reading of the ASIC ID causes trouble,
443                  * we reset the device
444                  */
445             	ps->IO.useEPPCmdMode = _FALSE;
446             	ps->sCaps.AsicID     = _ASIC_IS_98003;
447             	IOInitialize( ps );
448 			    IOSoftwareReset( ps );
449 
450    	            result = detectAsic98003( ps );
451        	        break;
452 
453             default:
454    				DBG( DBG_HIGH, "Unknown ASIC-ID\n" );
455        	        result = _E_NO_DEV;
456            	    break;
457             }
458 		}
459 
460 	} else {
461 
462         /* this will be called each time before operating on a previously
463          * detected device, to make sure we are still operating on the same one
464          */
465 		if( _ASIC_IS_98001 == mode ) {
466 
467 			DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 98001)\n" );
468 			result = detectAsic98001( ps );
469 
470         } else if( _ASIC_IS_98003 == mode ) {
471 
472 			DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 98003)\n" );
473 			result = detectAsic98003( ps );
474 
475 		} else {
476 
477 			DBG( DBG_HIGH, "Starting Scanner-detection (ASIC 96001/3)\n" );
478 			result = detectP48xx( ps );
479 		}
480 	}
481 
482 	if( _OK == result ) {
483 
484 		_ASSERT( ps->SetupScannerVariables );
485 		ps->SetupScannerVariables( ps );
486 
487 		detectSetupBuffers( ps );
488 
489 	} else {
490 /* CHECK - we should not need that anymore - paranoia code ??!!!!
491 */
492 		ps->sCaps.wIOBase = _NO_BASE;
493 	}
494 
495 	DBG( DBG_LOW, "*** DETECTION DONE, result: %i ***\n", result );
496 	return result;
497 }
498 
499 /* END PLUSTEK-PP_DETECT.C ..................................................*/
500