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