1 /* @file plustek-pp_ptdrv.c
2  * @brief this is the driver interface
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 - Added some comments
12  *        - added claiming/release of parallel port resources for this driver
13  *        - added scaling function for high resolution modes where dpix < dpiy
14  * - 0.32 - Revised lamp-off behaviour
15  *        - removed function ptdrvIsLampOn
16  *        - fixed misbehaviour when using cat /dev/pt_drv
17  *        - moved parport-functions to module misc.c
18  * - 0.33 - added parameter lOffonEnd
19  *        - revised parport concurrency
20  *        - removed calls to ps->PositionLamp
21  * - 0.34 - no changes
22  * - 0.35 - removed _PTDRV_PUT_SCANNER_MODEL from ioctl interface
23  *        - added Kevins' changes (MiscRestorePort)
24  *        - added parameter legal and function PtDrvLegalRequested()
25  * - 0.36 - removed a bug in the shutdown function
26  *        - removed all OP600P specific stuff because of the Primax tests
27  *        - added version code to ioctl interface
28  *        - added new parameter mov - model override
29  *        - removed parameter legal
30  *        - removed function PtDrvLegalRequested
31  *        - changes, due to define renaming
32  *        - patch for OpticPro 4800P
33  *        - added multiple device support
34  *        - added proc fs support/also for Kernel2.4
35  * - 0.37 - cleanup work, moved the procfs stuff to file procfs.c
36  *        - and some definitions to plustek_scan.h
37  *        - moved MODELSTR to misc.c
38  *        - output of the error-code after initialization
39  * - 0.38 - added P12 stuff
40  *        - removed function ptdrvIdleMode
41  *        - moved function ptdrvP96Calibration() to p48xxCalibration
42  *        - moved function ptdrvP98Calibration() to p9636Calibration
43  *        - added devfs support (patch by Gordon Heydon <gjheydon@bigfoot.com>)
44  * - 0.39 - added schedule stuff after reading one line to have a better
45  *          system response in SPP modes
46  *        - added forceMode switch
47  * - 0.40 - added MODULE_LICENSE stuff
48  * - 0.41 - added _PTDRV_ADJUST functionality
49  *        - changed ioctl call to PutImage
50  * - 0.42 - added _PTDRV_SETMAP functionality
51  *        - improved the cancel functionality
52  * - 0.43 - added LINUX_26 stuff
53  *        - changed include names
54  *        - changed version string stuff
55  * - 0.44 - added support for more recent kernels
56  *        - fix format string issues, as Long types default to int32_t
57  *          now
58  * .
59  * <hr>
60  * This file is part of the SANE package.
61  *
62  * This program is free software; you can redistribute it and/or
63  * modify it under the terms of the GNU General Public License as
64  * published by the Free Software Foundation; either version 2 of the
65  * License, or (at your option) any later version.
66  *
67  * This program is distributed in the hope that it will be useful, but
68  * WITHOUT ANY WARRANTY; without even the implied warranty of
69  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
70  * General Public License for more details.
71  *
72  * You should have received a copy of the GNU General Public License
73  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
74  *
75  * As a special exception, the authors of SANE give permission for
76  * additional uses of the libraries contained in this release of SANE.
77  *
78  * The exception is that, if you link a SANE library with other files
79  * to produce an executable, this does not by itself cause the
80  * resulting executable to be covered by the GNU General Public
81  * License.  Your use of that executable is in no way restricted on
82  * account of linking the SANE library code into it.
83  *
84  * This exception does not, however, invalidate any other reasons why
85  * the executable file might be covered by the GNU General Public
86  * License.
87  *
88  * If you submit changes to SANE to the maintainers to be included in
89  * a subsequent release, you agree by submitting the changes that
90  * those changes may be distributed with this exception intact.
91  *
92  * If you write modifications of your own for SANE, it is your choice
93  * whether to permit this exception to apply to your modifications.
94  * If you do not wish that, delete this exception notice.
95  * <hr>
96  */
97 #include "plustek-pp_scan.h"
98 
99 /****************************** static vars **********************************/
100 
101 /* default port is at 0x378 */
102 static int port[_MAX_PTDEVS] = { 0x378, 0, 0, 0 };
103 
104 static pScanData PtDrvDevices[_MAX_PTDEVS]= { NULL,   NULL,   NULL,   NULL   };
105 static int       lampoff[_MAX_PTDEVS]     = { 180,    180,    180,    180    };
106 static int       warmup[_MAX_PTDEVS]      = { 30,     30,     30,     30     };
107 static int       lOffonEnd[_MAX_PTDEVS]   = { 0,      0,      0,      0      };
108 static UShort    mov[_MAX_PTDEVS]         = { 0,      0,      0,      0      };
109 static UShort    forceMode[_MAX_PTDEVS]   = { 0,      0,      0,      0      };
110 
111 /* timers for warmup checks */
112 static TimerDef toTimer[_MAX_PTDEVS];
113 
114 static Bool	PtDrvInitialized = _FALSE;
115 #ifdef HAVE_SETITIMER
116 static struct itimerval saveSettings;
117 #endif
118 
119 
120 /****************************** some prototypes ******************************/
121 
122 static void ptdrvStartLampTimer( pScanData ps );
123 
124 /****************************** local functions ******************************/
125 
126 /** copy user-space data into kernel memory
127  */
getUserPtr(const pVoid useraddr, pVoid where, UInt size )128 static int getUserPtr(const pVoid useraddr, pVoid where, UInt size )
129 {
130 	int err = _OK;
131 
132 	/* do parameter checks */
133 	if((NULL == useraddr) || ( 0 == size))
134 		return _E_INVALID;
135 
136 	switch (size) {
137 	default:
138 		memcpy( where, useraddr, size );
139 	}
140 	return err;
141 }
142 
143 /** copy kernel data into user mode address space
144  */
putUserPtr( const pVoid ptr, pVoid useraddr, UInt size )145 static int putUserPtr( const pVoid ptr, pVoid useraddr, UInt size )
146 {
147 	int err = _OK;
148 
149 	if (NULL == useraddr)
150     	return _E_INVALID;
151 
152 	memcpy( useraddr, ptr, size );
153 
154 	return err;
155 }
156 
copy_from_user( pVoid dest, pVoid src, unsigned long len )157 static unsigned long copy_from_user( pVoid dest, pVoid src, unsigned long len )
158 {
159 	memcpy( dest, src, len );
160 	return 0;
161 }
162 
copy_to_user( pVoid dest, pVoid src, unsigned long len )163 static unsigned long copy_to_user( pVoid dest, pVoid src, unsigned long len )
164 {
165 	memcpy( dest, src, len );
166 	return 0;
167 }
168 
169 /**
170  */
putUserVal(const ULong value, pVoid useraddr, UInt size)171 static int putUserVal(const ULong value, pVoid useraddr, UInt size)
172 {
173 	if (NULL == useraddr)
174     	return _E_INVALID;
175 
176 	switch (size) {
177 
178 	case sizeof(UChar):
179 		*(pUChar)useraddr = (UChar)value;
180 		break;
181 	case sizeof(UShort):
182 		*(pUShort)useraddr = (UShort)value;
183 		break;
184 	case sizeof(ULong):
185 		*(pULong)useraddr = (ULong)value;
186 		break;
187 
188   	default:
189     	return _E_INVALID;
190 	}
191 	return 0;
192 }
193 
194 /** switch lamp 0 on
195  */
ptDrvSwitchLampOn( pScanData ps )196 static void ptDrvSwitchLampOn( pScanData ps )
197 {
198 	DBG( DBG_LOW, "Switching lamp 0 on.\n" );
199 
200 	if( _IS_ASIC98(ps->sCaps.AsicID)) {
201 
202 		ps->AsicReg.RD_ScanControl |= _SCAN_NORMALLAMP_ON;
203 
204 		ps->bLastLampStatus = _SCAN_NORMALLAMP_ON;
205 
206 	} else {
207 
208 		ps->AsicReg.RD_ScanControl |= ps->bLampOn;
209 		ps->bLastLampStatus = ps->bLampOn;
210 	}
211 
212 	IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);
213 }
214 
215 /** check the lamp warmup
216  */
ptdrvLampWarmup( pScanData ps )217 static void ptdrvLampWarmup( pScanData ps )
218 {
219 	Bool	 warmupNeeded;
220 	TimerDef timer;
221 
222 	if( 0 == ps->warmup )
223 		return;
224 
225 	warmupNeeded = _FALSE;
226 
227 	/*
228 	 * do we have to warmup again ? Timer has not elapsed...
229 	 */
230 	if( _OK == MiscCheckTimer( &toTimer[ps->devno] )) {
231 
232 		DBG( DBG_LOW, "Startup warmup needed!\n" );
233 		warmupNeeded = _TRUE;
234 	} else {
235 
236 		warmupNeeded = ps->fWarmupNeeded;
237 	}
238 
239 	if( warmupNeeded ) {
240 
241 		/*
242 		 * correct lamp should have been switched on but
243 		 * before doing anything else wait until warmup has been done
244 		 */
245 		DBG( DBG_LOW, "Waiting on warmup - %u s\n", ps->warmup );
246 
247 		MiscStartTimer( &timer, _SECOND * ps->warmup );
248 		while( !MiscCheckTimer( &timer )) {
249 
250 			/* on break, we setup the initial timer again... */
251 			if( _FALSE == ps->fScanningStatus ) {
252 				MiscStartTimer( &toTimer[ps->devno], (_SECOND * ps->warmup));
253 				return;
254 			}
255 		};
256 
257 	}
258 #ifdef DEBUG
259 	else {
260 		DBG( DBG_LOW, "No warm-up needed \n" );
261 	}
262 #endif
263 
264 	/*
265 	 * start a timer here again with only a second timeout
266 	 * because we need this one only for startup (Force timeout!!)
267 	 */
268 	MiscStartTimer( &toTimer[ps->devno], _SECOND );
269 }
270 
271 /**
272  */
ptdrvLampTimerIrq( int sig_num )273 static void ptdrvLampTimerIrq( int sig_num )
274 {
275 	pScanData ps;
276 
277 	DBG( DBG_HIGH, "!! IRQ !! Lamp-Timer stopped.\n" );
278 
279     _VAR_NOT_USED( sig_num );
280 	ps = PtDrvDevices[0];
281 
282 	/*
283 	 * paranoia check!
284 	 */
285 	if( NULL == ps )
286 		return;
287 
288 	if( _NO_BASE == ps->sCaps.wIOBase )
289 		return;
290 
291 	if( _IS_ASIC98(ps->sCaps.AsicID)) {
292 	    ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMPS_ON;
293 	} else {
294 		ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMP_ON;
295 	}
296 
297 	/* force warmup... */
298 	ps->bLastLampStatus = 0xFF;
299 
300 	/*
301 	 * claim parallel port if necessary...
302 	 * if the port is busy, restart the timer
303 	 */
304 	if( _OK != MiscClaimPort(ps)) {
305 		ptdrvStartLampTimer( ps );
306 		return;
307 	}
308 
309 	IOCmdRegisterToScanner( ps, ps->RegScanControl,
310 							    ps->AsicReg.RD_ScanControl );
311 	MiscReleasePort(ps);
312 }
313 
314 /**
315  */
ptdrvStartLampTimer( pScanData ps )316 static void ptdrvStartLampTimer( pScanData ps )
317 {
318 	sigset_t 		 block, pause_mask;
319 	struct sigaction s;
320 #ifdef HAVE_SETITIMER
321 	struct itimerval interval;
322 #endif
323 
324 	/* block SIGALRM */
325 	sigemptyset( &block );
326 	sigaddset  ( &block, SIGALRM );
327 	sigprocmask( SIG_BLOCK, &block, &pause_mask );
328 
329 	/* setup handler */
330 	sigemptyset( &s.sa_mask );
331 	sigaddset  (  &s.sa_mask, SIGINT );
332 	s.sa_flags   = 0;
333 	s.sa_handler = ptdrvLampTimerIrq;
334 
335 	if(	sigaction( SIGALRM, &s, NULL ) < 0 ) {
336 		DBG(DBG_HIGH,"pt_drv%u: Can't setup timer-irq handler\n",ps->devno);
337 	}
338 
339 	sigprocmask( SIG_UNBLOCK, &block, &pause_mask );
340 
341 #ifdef HAVE_SETITIMER
342 	/*
343 	 * define a one-shot timer
344 	 */
345 	interval.it_value.tv_usec = 0;
346 	interval.it_value.tv_sec  = ps->lampoff;
347 	interval.it_interval.tv_usec = 0;
348 	interval.it_interval.tv_sec  = 0;
349 
350 	if( 0 != ps->lampoff )
351 		setitimer( ITIMER_REAL, &interval, &saveSettings );
352 #else
353 	alarm( ps->lampoff );
354 #endif
355 
356 	DBG( DBG_HIGH, "Lamp-Timer started!\n" );
357 }
358 
359 /**
360  */
ptdrvStopLampTimer( pScanData ps )361 static void ptdrvStopLampTimer( pScanData ps )
362 {
363 	sigset_t block, pause_mask;
364 
365 	/* block SIGALRM */
366 	sigemptyset( &block );
367 	sigaddset  ( &block, SIGALRM );
368 	sigprocmask( SIG_BLOCK, &block, &pause_mask );
369 #ifdef HAVE_SETITIMER
370 	if( 0 != ps->lampoff )
371 		setitimer( ITIMER_REAL, &saveSettings, NULL );
372 #else
373 	_VAR_NOT_USED( ps );
374 	alarm(0);
375 #endif
376 
377 	DBG( DBG_HIGH, "Lamp-Timer stopped!\n" );
378 }
379 
380 /** claim and initialize the requested port
381  */
ptdrvOpen( pScanData ps, int portBase )382 static int ptdrvOpen( pScanData ps, int portBase )
383 {
384 	int retval;
385 
386 	DBG( DBG_HIGH, "ptdrvOpen(port=0x%x)\n", (int32_t)portBase );
387 	if( NULL == ps )
388 		return _E_NULLPTR;
389 
390 	/*
391 	 * claim port resources...
392 	 */
393 	retval = MiscClaimPort(ps);
394 
395 	if( _OK != retval )
396 		return retval;
397 
398 	return MiscInitPorts( ps, portBase );
399 }
400 
401 /** free used memory (if necessary)
402  * restore the parallel port settings and release the port
403  */
ptdrvClose( pScanData ps )404 static int ptdrvClose( pScanData ps )
405 {
406 	DBG( DBG_HIGH, "ptdrvClose()\n" );
407 	if( NULL == ps )
408 		return _E_NULLPTR;
409 
410 	/*
411 	 * should be cleared by ioctl(close)
412 	 */
413     if ( NULL != ps->driverbuf ) {
414 		DBG( DBG_LOW, "*** cleanup buffers ***\n" );
415         _VFREE( ps->driverbuf );
416         ps->driverbuf = NULL;
417     }
418 
419     if ( NULL != ps->Shade.pHilight ) {
420         _VFREE( ps->Shade.pHilight );
421         ps->Shade.pHilight = NULL;
422     }
423 
424 	/*
425 	 * restore/release port resources...
426 	 */
427 	MiscRestorePort( ps );
428 	MiscReleasePort( ps );
429 
430 	return _OK;
431 }
432 
433 /** will be called during OPEN_DEVICE ioctl call
434  */
ptdrvOpenDevice( pScanData ps )435 static int ptdrvOpenDevice( pScanData ps )
436 {
437 	int    retval, iobase;
438 	UShort asic;
439 	UChar  lastStat;
440 	UShort lastMode;
441 	ULong  devno;
442 
443     int pd;
444 
445 	/*
446 	 * push some values from the struct
447      */
448 	pd       = ps->pardev;
449 	iobase   = ps->sCaps.wIOBase;
450 	asic     = ps->sCaps.AsicID;
451 	lastStat = ps->bLastLampStatus;
452 	lastMode = ps->IO.lastPortMode;
453 	devno    = ps->devno;
454 
455 	/*
456 	 * reinit the show
457 	 */
458 	ptdrvStopLampTimer( ps );
459 	MiscReinitStruct  ( ps );
460 
461 	/*
462 	 * pop the val(s)
463 	 */
464 	ps->pardev          = pd;
465 	ps->bLastLampStatus = lastStat;
466 	ps->IO.lastPortMode = lastMode;
467 	ps->devno           = devno;
468 	ps->ModelOverride = mov[devno];
469 	ps->warmup        = warmup[devno];
470 	ps->lampoff		  = lampoff[devno];
471 	ps->lOffonEnd	  = lOffonEnd[devno];
472 	ps->IO.forceMode  = forceMode[devno];
473 
474 	/*
475 	 * try to find scanner again
476 	 */
477 	retval = ptdrvOpen( ps, iobase );
478 
479 	if( _OK == retval )
480 		retval = DetectScanner( ps, asic );
481 	else
482 		ptdrvStartLampTimer( ps );
483 
484 	return retval;
485 }
486 
487 /*.............................................................................
488  * initialize the driver
489  * allocate memory for the ScanData structure and do some presets
490  */
ptdrvInit( int devno )491 static int ptdrvInit( int devno )
492 {
493 	int       retval;
494 	pScanData ps;
495 
496 	DBG( DBG_HIGH, "ptdrvInit(%u)\n", devno );
497 
498 	if( devno >= _MAX_PTDEVS )
499 		return _E_NO_DEV;
500 
501 	/*
502 	 * allocate memory for our large ScanData-structure
503 	 */
504 	ps = MiscAllocAndInitStruct();
505 	if( NULL == ps ) {
506 		return _E_ALLOC;
507 	}
508 
509 	ps->ModelOverride = mov[devno];
510 	ps->warmup        = warmup[devno];
511 	ps->lampoff       = lampoff[devno];
512 	ps->lOffonEnd     = lOffonEnd[devno];
513 	ps->IO.forceMode  = forceMode[devno];
514 	ps->devno         = devno;
515 
516 	/* assign it right here, to allow correct shutdown */
517 	PtDrvDevices[devno] = ps;
518 
519 	/*
520 	 * try to register the port
521 	 */
522 	retval = MiscRegisterPort( ps, port[devno] );
523 
524 	if( _OK == retval ) {
525 		retval = ptdrvOpen( ps, port[devno] );
526 	}
527 
528 	/*
529 	 * try to detect a scanner...
530 	 */
531 	if( _OK == retval ) {
532 		retval = DetectScanner( ps, 0 );
533 
534 		/* do this here before releasing the port */
535 		if( _OK == retval ) {
536 			ptDrvSwitchLampOn( ps );
537 		}
538 		ptdrvClose( ps );
539 	}
540 
541 	if( _OK == retval ) {
542 
543 		DBG( DBG_LOW, "pt_drv%u: %s found\n",
544 									 devno, MiscGetModelName(ps->sCaps.Model));
545 
546 		/*
547 		 * initialize the timespan timer
548 	     */
549 		MiscStartTimer( &toTimer[ps->devno], (_SECOND * ps->warmup));
550 
551 		if( 0 == ps->lampoff )
552 		DBG( DBG_LOW,
553 					"pt_drv%u: Lamp-Timer switched off.\n", devno );
554 		else {
555 		DBG( DBG_LOW,
556 					"pt_drv%u: Lamp-Timer set to %u seconds.\n",
557 														devno, ps->lampoff );
558 		}
559 
560 		DBG( DBG_LOW,
561 				"pt_drv%u: WarmUp period set to %u seconds.\n",
562 														devno, ps->warmup );
563 
564 		if( 0 == ps->lOffonEnd ) {
565 		DBG( DBG_LOW,
566 				"pt_drv%u: Lamp untouched on driver unload.\n", devno );
567 		} else {
568 		DBG( DBG_LOW,
569 				"pt_drv%u: Lamp switch-off on driver unload.\n", devno );
570 		}
571 
572 		ptdrvStartLampTimer( ps );
573 	}
574 
575 	return retval;
576 }
577 
578 /*.............................................................................
579  * shutdown the driver:
580  * switch the lights out
581  * stop the motor
582  * free memory
583  */
ptdrvShutdown( pScanData ps )584 static int ptdrvShutdown( pScanData ps )
585 {
586 	int devno;
587 
588 	DBG( DBG_HIGH, "ptdrvShutdown()\n" );
589 
590 	if( NULL == ps )
591 		return _E_NULLPTR;
592 
593 	devno = ps->devno;
594 
595 	DBG( DBG_HIGH, "cleanup device %u\n", devno );
596 
597 	if( _NO_BASE != ps->sCaps.wIOBase ) {
598 
599 		ptdrvStopLampTimer( ps );
600 
601 		if( _OK == MiscClaimPort(ps)) {
602 
603 			ps->PutToIdleMode( ps );
604 
605 			if( 0 != ps->lOffonEnd ) {
606 				if( _IS_ASIC98(ps->sCaps.AsicID)) {
607 		            ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMPS_ON;
608 				} else {
609 					ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMP_ON;
610 	        	}
611 				IOCmdRegisterToScanner( ps, ps->RegScanControl,
612 											  ps->AsicReg.RD_ScanControl );
613 			}
614 		}
615 		MiscReleasePort( ps );
616 	}
617 
618 	/* unregister the driver
619 	 */
620 	MiscUnregisterPort( ps );
621 
622 	_KFREE( ps );
623 	if( devno < _MAX_PTDEVS )
624 		PtDrvDevices[devno] = NULL;
625 
626 	return _OK;
627 }
628 
629 /*.............................................................................
630  * the IOCTL interface
631  */
ptdrvIoctl( pScanData ps, UInt cmd, pVoid arg )632 static int ptdrvIoctl( pScanData ps, UInt cmd, pVoid arg )
633 {
634 	UShort dir;
635 	UShort version;
636 	UInt   size;
637 	ULong  argVal;
638 	int    cancel;
639 	int    retval;
640 
641 	/*
642  	 * do the preliminary stuff here
643 	 */
644 	if( NULL == ps )
645 		return _E_NULLPTR;
646 
647 	retval = _OK;
648 
649 	dir  = _IOC_DIR(cmd);
650 	size = _IOC_SIZE(cmd);
651 
652 	if ((_IOC_WRITE == dir) && size && (size <= sizeof(ULong))) {
653 
654     	if (( retval = getUserPtr( arg, &argVal, size))) {
655 			DBG( DBG_HIGH, "ioctl() failed - result = %i\n", retval );
656       		return retval;
657 		}
658 	}
659 
660 	switch( cmd ) {
661 
662 	/* open */
663     case _PTDRV_OPEN_DEVICE:
664 		DBG( DBG_LOW, "ioctl(_PTDRV_OPEN_DEVICE)\n" );
665    	  	if (copy_from_user(&version, arg, sizeof(UShort)))
666 			return _E_FAULT;
667 
668 		if( _PTDRV_IOCTL_VERSION != version ) {
669 			DBG( DBG_HIGH, "Version mismatch: Backend=0x%04X(0x%04X)",
670 							version, _PTDRV_IOCTL_VERSION );
671 			return _E_VERSION;
672 		}
673 
674 		retval = ptdrvOpenDevice( ps );
675       	break;
676 
677 	/* close */
678 	case _PTDRV_CLOSE_DEVICE:
679 		DBG( DBG_LOW,  "ioctl(_PTDRV_CLOSE_DEVICE)\n" );
680 
681 	    if ( NULL != ps->driverbuf ) {
682 			DBG( DBG_LOW, "*** cleanup buffers ***\n" );
683 	        _VFREE( ps->driverbuf );
684     	    ps->driverbuf = NULL;
685 	    }
686 
687     	if ( NULL != ps->Shade.pHilight ) {
688         	_VFREE( ps->Shade.pHilight );
689 	        ps->Shade.pHilight = NULL;
690     	}
691 
692 		ps->PutToIdleMode( ps );
693 		ptdrvStartLampTimer( ps );
694       	break;
695 
696 	/* get caps - no scanner connection necessary */
697     case _PTDRV_GET_CAPABILITIES:
698 		DBG( DBG_LOW, "ioctl(_PTDRV_GET_CAPABILITES)\n" );
699 
700     	return putUserPtr( &ps->sCaps, arg, size);
701       	break;
702 
703 	/* get lens-info - no scanner connection necessary */
704     case _PTDRV_GET_LENSINFO:
705 		DBG( DBG_LOW, "ioctl(_PTDRV_GET_LENSINFO)\n" );
706 
707       	return putUserPtr( &ps->LensInf, arg, size);
708       	break;
709 
710 	/* put the image info - no scanner connection necessary */
711     case _PTDRV_PUT_IMAGEINFO:
712       	{
713             short  tmpcx, tmpcy;
714       		ImgDef img;
715 
716 			DBG( DBG_LOW, "ioctl(_PTDRV_PUT_IMAGEINFO)\n" );
717 			if (copy_from_user( &img, (pImgDef)arg, size))
718 				return _E_FAULT;
719 
720             tmpcx = (short)img.crArea.cx;
721             tmpcy = (short)img.crArea.cy;
722 
723 			if(( 0 >= tmpcx ) || ( 0 >= tmpcy )) {
724 				DBG( DBG_LOW, "CX or CY <= 0!!\n" );
725 				return _E_INVALID;
726 			}
727 
728 			_ASSERT( ps->GetImageInfo );
729 	      	ps->GetImageInfo( ps, &img );
730 		}
731       	break;
732 
733 	/* get crop area - no scanner connection necessary */
734     case _PTDRV_GET_CROPINFO:
735     	{
736       		CropInfo	outBuffer;
737       		pCropInfo	pcInf = &outBuffer;
738 
739 			DBG( DBG_LOW, "ioctl(_PTDRV_GET_CROPINFO)\n" );
740 
741 			memset( pcInf, 0, sizeof(CropInfo));
742 
743 	      	pcInf->dwPixelsPerLine = ps->DataInf.dwAppPixelsPerLine;
744     	  	pcInf->dwBytesPerLine  = ps->DataInf.dwAppBytesPerLine;
745       		pcInf->dwLinesPerArea  = ps->DataInf.dwAppLinesPerArea;
746       		return putUserPtr( pcInf, arg, size );
747       	}
748       	break;
749 
750 	/* adjust the driver settings */
751 	case _PTDRV_ADJUST:
752 		{
753 			PPAdjDef adj;
754 
755 			DBG( DBG_LOW, "ioctl(_PTDRV_ADJUST)\n" );
756 
757 			if (copy_from_user(&adj, (pPPAdjDef)arg, sizeof(PPAdjDef)))
758 				return _E_FAULT;
759 
760 			DBG( DBG_LOW, "Adjusting device %u\n", ps->devno );
761 			DBG( DBG_LOW, "warmup:       %i\n", adj.warmup );
762 			DBG( DBG_LOW, "lampOff:      %i\n", adj.lampOff );
763 			DBG( DBG_LOW, "lampOffOnEnd: %i\n", adj.lampOffOnEnd );
764 
765 			if( ps->devno < _MAX_PTDEVS ) {
766 
767 				if( adj.warmup >= 0 ) {
768 					warmup[ps->devno] = adj.warmup;
769 					ps->warmup        = adj.warmup;
770 				}
771 
772 				if( adj.lampOff >= 0 ) {
773 					lampoff[ps->devno] = adj.lampOff;
774 					ps->lampoff        = adj.lampOff;
775 				}
776 
777 				if( adj.lampOffOnEnd >= 0 ) {
778 					lOffonEnd[ps->devno] = adj.lampOffOnEnd;
779 					ps->lOffonEnd        = adj.lampOffOnEnd;
780 				}
781 			}
782 		}
783 		break;
784 
785 	/* set a specific map (r,g,b or gray) */
786 	case _PTDRV_SETMAP:
787 		{
788 			int     i, x_len;
789 			MapDef  map;
790 
791 			DBG( DBG_LOW, "ioctl(_PTDRV_SETMAP)\n" );
792 
793 			if (copy_from_user( &map, (pMapDef)arg, sizeof(MapDef)))
794 				return _E_FAULT;
795 
796 			DBG( DBG_LOW, "maplen=%u, mapid=%u, addr=0x%08lx\n",
797 							map.len, map.map_id, (u_long)map.map );
798 
799 			x_len = 256;
800 			if( _IS_ASIC98(ps->sCaps.AsicID))
801 				x_len = 4096;
802 
803 			/* check for 0 pointer and len */
804 			if((NULL == map.map) || (x_len != map.len)) {
805 				DBG( DBG_LOW, "map pointer == 0, or map len invalid!!\n" );
806 				return _E_INVALID;
807 			}
808 
809     		if( _MAP_MASTER == map.map_id ) {
810 
811 				for( i = 0; i < 3; i++ ) {
812 					if (copy_from_user((pVoid)&ps->a_bMapTable[x_len * i],
813 					                    map.map, x_len )) {
814 						return _E_FAULT;
815 					}
816 				}
817 			} else {
818 
819 				u_long idx = 0;
820 				if( map.map_id == _MAP_GREEN )
821 					idx = 1;
822 				if( map.map_id == _MAP_BLUE )
823 					idx = 2;
824 
825 				if (copy_from_user((pVoid)&ps->a_bMapTable[x_len * idx],
826 				                   map.map, x_len )) {
827 						return _E_FAULT;
828 				}
829 			}
830 
831 			/* here we adjust the maps according to
832 			 * the brightness and contrast settings
833 			 */
834 			MapAdjust( ps, map.map_id );
835 		}
836 		break;
837 
838 	/* set environment - no scanner connection necessary */
839     case _PTDRV_SET_ENV:
840       	{
841 			ScanInfo sInf;
842 
843 			DBG( DBG_LOW, "ioctl(_PTDRV_SET_ENV)\n" );
844 
845 			if (copy_from_user(&sInf, (pScanInfo)arg, sizeof(ScanInfo)))
846 				return _E_FAULT;
847 
848 			/*
849 			 * to make the OpticPro 4800P work, we need to invert the
850 			 * Inverse flag
851 			 */
852 			if( _ASIC_IS_96001 == ps->sCaps.AsicID ) {
853 				if( SCANDEF_Inverse & sInf.ImgDef.dwFlag )
854 					sInf.ImgDef.dwFlag &= ~SCANDEF_Inverse;
855 				else
856 					sInf.ImgDef.dwFlag |= SCANDEF_Inverse;
857 			}
858 
859 			_ASSERT( ps->SetupScanSettings );
860       		retval = ps->SetupScanSettings( ps, &sInf );
861 
862 			/* CHANGE preset map here */
863 			if( _OK == retval ) {
864 				MapInitialize ( ps );
865 				MapSetupDither( ps );
866 
867 				ps->DataInf.dwVxdFlag |= _VF_ENVIRONMENT_READY;
868 
869 				if (copy_to_user((pScanInfo)arg, &sInf, sizeof(ScanInfo)))
870 					return _E_FAULT;
871 			}
872 		}
873 		break;
874 
875 	/* start scan */
876 	case _PTDRV_START_SCAN:
877 		{
878 			StartScan  outBuffer;
879 			pStartScan pstart = (pStartScan)&outBuffer;
880 
881 			DBG( DBG_LOW, "ioctl(_PTDRV_START_SCAN)\n" );
882 
883 			retval = IOIsReadyForScan( ps );
884 			if( _OK == retval ) {
885 
886 				ps->dwDitherIndex      = 0;
887 				ps->fScanningStatus    = _TRUE;
888 				pstart->dwBytesPerLine = ps->DataInf.dwAppBytesPerLine;
889 				pstart->dwLinesPerScan = ps->DataInf.dwAppLinesPerArea;
890 				pstart->dwFlag 		   = ps->DataInf.dwScanFlag;
891 
892 				ps->DataInf.dwVxdFlag |= _VF_FIRSTSCANLINE;
893 				ps->DataInf.dwScanFlag&=~(_SCANNER_SCANNING|_SCANNER_PAPEROUT);
894 
895 				if (copy_to_user((pStartScan)arg, pstart, sizeof(StartScan)))
896 					return _E_FAULT;
897 			}
898 		}
899 		break;
900 
901 	/* stop scan */
902     case _PTDRV_STOP_SCAN:
903 
904 		DBG( DBG_LOW, "ioctl(_PTDRV_STOP_SCAN)\n" );
905 
906 		if (copy_from_user(&cancel, arg, sizeof(short)))
907 			return _E_FAULT;
908 
909 		/* we may use this to abort scanning! */
910 		ps->fScanningStatus = _FALSE;
911 
912 		/* when using this to cancel, then that's all */
913 		if( _FALSE == cancel ) {
914 
915 			MotorToHomePosition( ps );
916 
917     		ps->DataInf.dwAppLinesPerArea = 0;
918       		ps->DataInf.dwScanFlag &= ~_SCANNER_SCANNING;
919 
920 			/* if environment was never set */
921     	  	if (!(ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY))
922         		retval = _E_SEQUENCE;
923 
924 	      	ps->DataInf.dwVxdFlag &= ~_VF_ENVIRONMENT_READY;
925 
926 		} else {
927 			DBG( DBG_LOW, "CANCEL Mode set\n" );
928 		}
929 		retval = putUserVal(retval, arg, size);
930       	break;
931 
932 	/* read the flag status register, when reading the action button, you must
933 	 * only do this call and none of the other ioctl's
934      * like open, etc or it will always show up as "1"
935 	 */
936 	case _PTDRV_ACTION_BUTTON:
937 		DBG( DBG_LOW, "ioctl(_PTDRV_ACTION_BUTTON)\n" );
938 		IODataRegisterFromScanner( ps, ps->RegStatus );
939       	retval = putUserVal( argVal, arg, size );
940 		break;
941 
942 	default:
943 		retval = _E_NOSUPP;
944       	break;
945 	}
946 
947 	return retval;
948 }
949 
950 /*.............................................................................
951  * read the data
952  */
ptdrvRead( pScanData ps, pUChar buffer, int count )953 static int ptdrvRead( pScanData ps, pUChar buffer, int count )
954 {
955 	pUChar	scaleBuf;
956 	ULong	dwLinesRead = 0;
957 	int 	retval      = _OK;
958 
959 #ifdef _ASIC_98001_SIM
960 		DBG( DBG_LOW,
961 					"pt_drv : Software-Emulation active, can't read!\n" );
962 	return _E_INVALID;
963 #endif
964 
965 	if((NULL == buffer) || (NULL == ps)) {
966 		DBG( DBG_HIGH,
967 						"pt_drv :  Internal NULL-pointer!\n" );
968 		return _E_NULLPTR;
969 	}
970 
971 	if( 0 == count ) {
972 		DBG( DBG_HIGH,
973 			"pt_drv%u: reading 0 bytes makes no sense!\n", ps->devno );
974 		return _E_INVALID;
975 	}
976 
977 	if( _FALSE == ps->fScanningStatus )
978 		return _E_ABORT;
979 
980 	/*
981 	 * has the environment been set ?
982 	 * this should prevent the driver from causing a seg-fault
983 	 * when using the cat /dev/pt_drv command!
984 	 */
985    	if (!(ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY)) {
986 		DBG( DBG_HIGH,
987 			"pt_drv%u:  Cannot read, driver not initialized!\n",ps->devno);
988 		return _E_SEQUENCE;
989 	}
990 
991 	/*
992 	 * get some memory
993 	 */
994 	ps->Scan.bp.pMonoBuf = _KALLOC( ps->DataInf.dwAppPhyBytesPerLine, GFP_KERNEL);
995 
996 	if ( NULL == ps->Scan.bp.pMonoBuf ) {
997 		DBG( DBG_HIGH,
998 			"pt_drv%u:  Not enough memory available!\n", ps->devno );
999     	return _E_ALLOC;
1000 	}
1001 
1002 	/* if we have to do some scaling, we need another buffer... */
1003 	if( ps->DataInf.XYRatio > 1000 ) {
1004 
1005 		scaleBuf = _KALLOC( ps->DataInf.dwAppPhyBytesPerLine, GFP_KERNEL);
1006 		if ( NULL == scaleBuf ) {
1007 			_KFREE( ps->Scan.bp.pMonoBuf );
1008 		DBG( DBG_HIGH,
1009 			"pt_drv%u:  Not enough memory available!\n", ps->devno );
1010     		return _E_ALLOC;
1011 		}
1012 	} else {
1013 		scaleBuf = NULL;
1014 	}
1015 
1016 	DBG( DBG_LOW, "PtDrvRead(%u bytes)*****************\n", count );
1017 	DBG( DBG_LOW, "MonoBuf = 0x%08lx[%u], scaleBuf = 0x%lx\n",
1018 			(unsigned long)ps->Scan.bp.pMonoBuf,
1019             ps->DataInf.dwAppPhyBytesPerLine, (unsigned long)scaleBuf );
1020 
1021 	/*
1022 	 * in case of a previous problem, move the sensor back home
1023 	 */
1024   	MotorToHomePosition( ps );
1025 
1026 	if( _FALSE == ps->fScanningStatus ) {
1027 		retval = _E_ABORT;
1028 		goto ReadFinished;
1029 	}
1030 
1031 	dwLinesRead = 0;
1032 
1033 	/*
1034 	 * first of all calibrate the show
1035 	 */
1036 	ps->bMoveDataOutFlag   = _DataInNormalState;
1037    	ps->fHalfStepTableFlag = _FALSE;
1038     ps->fReshaded          = _FALSE;
1039    	ps->fScanningStatus    = _TRUE;
1040 
1041     if( _ASIC_IS_98003 == ps->sCaps.AsicID )
1042         ps->Scan.fRefreshState = _FALSE;
1043     else
1044         ps->Scan.fRefreshState = _TRUE;
1045 
1046     ptdrvLampWarmup( ps );
1047 
1048 	if( _FALSE == ps->fScanningStatus ) {
1049 		retval = _E_ABORT;
1050 		goto ReadFinished;
1051 	}
1052 
1053     retval = ps->Calibration( ps );
1054 	if( _OK != retval ) {
1055 		DBG( DBG_HIGH,
1056 			"pt_drv%u: calibration failed, result = %i\n",
1057 														ps->devno, retval );
1058 		goto ReadFinished;
1059 	}
1060 
1061     if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {
1062 
1063         ps->OpenScanPath( ps );
1064 
1065     	MotorP98003ForceToLeaveHomePos( ps );
1066     }
1067 
1068 	_ASSERT(ps->SetupScanningCondition);
1069   	ps->SetupScanningCondition(ps);
1070 
1071     if( _ASIC_IS_98003 != ps->sCaps.AsicID ) {
1072     	ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE );
1073         IOSetToMotorRegister( ps );
1074     } else {
1075 
1076         ps->WaitForPositionY( ps );
1077     	_DODELAY( 70 );
1078     	ps->Scan.bOldScanState = IOGetScanState( ps, _TRUE ) & _SCANSTATE_MASK;
1079     }
1080 
1081     ps->DataInf.dwScanFlag |= _SCANNER_SCANNING;
1082 
1083 	if( _FALSE == ps->fScanningStatus ) {
1084 		DBG( DBG_HIGH, "read aborted!\n" );
1085 		retval = _E_ABORT;
1086 		goto ReadFinished;
1087 	}
1088 
1089 	/*
1090 	 * now get the picture data
1091 	 */
1092 	DBG( DBG_HIGH, "dwAppLinesPerArea = %d\n", ps->DataInf.dwAppLinesPerArea);
1093 	DBG( DBG_HIGH, "dwAppBytesPerLine = %d\n", ps->DataInf.dwAppBytesPerLine);
1094 
1095 /* HEINER: A3I
1096 	ps->bMoveDataOutFlag = _DataFromStopState;
1097 */
1098   	if ( 0 != ps->DataInf.dwAppLinesPerArea ) {
1099 
1100 	   	ps->Scan.dwLinesToRead = count / ps->DataInf.dwAppBytesPerLine;
1101 
1102     	if( ps->Scan.dwLinesToRead ) {
1103 
1104         	DBG( DBG_HIGH, "dwLinesToRead = %d\n", ps->Scan.dwLinesToRead );
1105 
1106       		if( ps->Scan.dwLinesToRead > ps->DataInf.dwAppLinesPerArea )
1107         		ps->Scan.dwLinesToRead = ps->DataInf.dwAppLinesPerArea;
1108 
1109       		ps->DataInf.dwAppLinesPerArea -= ps->Scan.dwLinesToRead;
1110 
1111       		if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle)
1112         		buffer += ((ps->Scan.dwLinesToRead - 1) *
1113                    				ps->DataInf.dwAppBytesPerLine);
1114 
1115       		if (ps->DataInf.dwVxdFlag & _VF_DATATOUSERBUFFER)
1116         		ps->DataInf.pCurrentBuffer = ps->Scan.bp.pMonoBuf;
1117 
1118       		while(ps->fScanningStatus && ps->Scan.dwLinesToRead) {
1119 
1120         		_ASSERT(ps->ReadOneImageLine);
1121 		   		if (!ps->ReadOneImageLine(ps)) {
1122         			ps->fScanningStatus = _FALSE;
1123             		DBG( DBG_HIGH, "ReadOneImageLine() failed at line %u!\n",
1124                                     dwLinesRead );
1125 					break;
1126 				}
1127 
1128 				/*
1129 				 * as we might scan images that exceed the CCD-capabilities
1130 				 * in x-resolution, we have to enlarge the line data
1131 				 * i.e.: scanning at 1200dpi generates on a P9636 600 dpi in
1132 				 *       x-direction but 1200dpi in y-direction...
1133 				 */
1134 				if( NULL != scaleBuf ) {
1135 					ScaleX( ps, ps->Scan.bp.pMonoBuf, scaleBuf );
1136 	    	    	if (copy_to_user( buffer, scaleBuf,
1137 									ps->DataInf.dwAppPhyBytesPerLine)) {
1138 						return _E_FAULT;
1139 					}
1140 				} else {
1141 	    	    	if (copy_to_user( buffer, ps->Scan.bp.pMonoBuf,
1142 								  ps->DataInf.dwAppPhyBytesPerLine)) {
1143 						return _E_FAULT;
1144 					}
1145 				}
1146 
1147 				buffer += ps->Scan.lBufferAdjust;
1148 				dwLinesRead++;
1149 				ps->Scan.dwLinesToRead--;
1150 
1151 				/* needed, esp. to avoid freezing the system in SPP mode */
1152 /*#else
1153 				sched_yield();
1154 */
1155         	}
1156 
1157 			if (ps->fScanningStatus) {
1158 
1159             	if( _IS_ASIC96(ps->sCaps.AsicID))
1160           			MotorP96SetSpeedToStopProc(ps);
1161 
1162 			} else {
1163         		if (ps->DataInf.dwScanFlag & (SCANDEF_StopWhenPaperOut |
1164                     	                         SCANDEF_UnlimitLength)) {
1165           			ps->DataInf.dwAppLinesPerArea = 0;
1166 				} else {
1167         			if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle)
1168             			buffer -= (ps->DataInf.dwAppBytesPerLine *
1169                 	    		   (ps->Scan.dwLinesToRead - 1));
1170           			memset( buffer, 0xff,
1171           		   			ps->Scan.dwLinesToRead * ps->DataInf.dwAppBytesPerLine );
1172         	  		dwLinesRead += ps->Scan.dwLinesToRead;
1173     	    	}
1174 	        }
1175 
1176       	} else {
1177       		retval = _E_INTERNAL;
1178 		}
1179 	}
1180 
1181 	if( _FALSE == ps->fScanningStatus ) {
1182 		DBG( DBG_HIGH, "read aborted!\n" );
1183 		retval = _E_ABORT;
1184 	}
1185 
1186 ReadFinished:
1187 
1188 
1189 	if( _ASIC_IS_98003 == ps->sCaps.AsicID )
1190 		ps->CloseScanPath( ps );
1191 
1192 	if( NULL != ps->Scan.bp.pMonoBuf )
1193 		_KFREE( ps->Scan.bp.pMonoBuf );
1194 
1195 	if( NULL != scaleBuf )
1196 		_KFREE( scaleBuf );
1197 
1198 	/*
1199 	 * on success return number of bytes red
1200 	 */
1201 	if ( _OK == retval )
1202     	return (ps->DataInf.dwAppPhyBytesPerLine * dwLinesRead);
1203 
1204    	return retval;
1205 }
1206 
1207 /*.............................................................................
1208  * here we only have wrapper functions
1209  */
PtDrvInit( const char *dev_name, UShort model_override )1210 static int PtDrvInit( const char *dev_name, UShort model_override )
1211 {
1212 	int fd;
1213 	int result = _OK;
1214 
1215 	if( _TRUE == PtDrvInitialized )
1216 		return _OK;
1217 
1218 	result = sanei_pp_open( dev_name, &fd );
1219 	if( SANE_STATUS_GOOD != result )
1220 		return result;
1221 
1222 	port[0] = fd;
1223 	mov[0]  = model_override;
1224 
1225 	result = ptdrvInit( 0 );
1226 
1227 	if( _OK == result ) {
1228 		PtDrvInitialized = _TRUE;
1229 	} else {
1230 		ptdrvShutdown( PtDrvDevices[0] );
1231 	}
1232 
1233 	return result;
1234 }
1235 
PtDrvShutdown( void )1236 static int PtDrvShutdown( void )
1237 {
1238 	int result;
1239 
1240 	if( _FALSE == PtDrvInitialized )
1241 		return _E_NOT_INIT;
1242 
1243 	result = ptdrvShutdown( PtDrvDevices[0] );
1244 
1245 	PtDrvInitialized = _FALSE;
1246 
1247 	return result;
1248 }
1249 
PtDrvOpen( void )1250 static int PtDrvOpen( void )
1251 {
1252 	if( _FALSE == PtDrvInitialized )
1253 		return _E_NOT_INIT;
1254 
1255 	return _OK;
1256 }
1257 
PtDrvClose( void )1258 static int PtDrvClose( void )
1259 {
1260 	if( _FALSE == PtDrvInitialized )
1261 		return _E_NOT_INIT;
1262 
1263 	return ptdrvClose( PtDrvDevices[0] );
1264 }
1265 
PtDrvIoctl( UInt cmd, pVoid arg )1266 static int PtDrvIoctl( UInt cmd, pVoid arg )
1267 {
1268 	if( _FALSE == PtDrvInitialized )
1269 		return _E_NOT_INIT;
1270 
1271 	return ptdrvIoctl( PtDrvDevices[0], cmd, arg);
1272 }
1273 
PtDrvRead( pUChar buffer, int count )1274 static int PtDrvRead ( pUChar buffer, int count )
1275 {
1276 	if( _FALSE == PtDrvInitialized )
1277 		return _E_NOT_INIT;
1278 
1279 	return ptdrvRead( PtDrvDevices[0], buffer, count );
1280 }
1281 
1282 /* END PLUSTEK-PP_PTDRV.C ...................................................*/
1283