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 */
102static int port[_MAX_PTDEVS] = { 0x378, 0, 0, 0 };
103
104static pScanData PtDrvDevices[_MAX_PTDEVS]= { NULL,   NULL,   NULL,   NULL   };
105static int       lampoff[_MAX_PTDEVS]     = { 180,    180,    180,    180    };
106static int       warmup[_MAX_PTDEVS]      = { 30,     30,     30,     30     };
107static int       lOffonEnd[_MAX_PTDEVS]   = { 0,      0,      0,      0      };
108static UShort    mov[_MAX_PTDEVS]         = { 0,      0,      0,      0      };
109static UShort    forceMode[_MAX_PTDEVS]   = { 0,      0,      0,      0      };
110
111/* timers for warmup checks */
112static TimerDef toTimer[_MAX_PTDEVS];
113
114static Bool	PtDrvInitialized = _FALSE;
115#ifdef HAVE_SETITIMER
116static struct itimerval saveSettings;
117#endif
118
119
120/****************************** some prototypes ******************************/
121
122static void ptdrvStartLampTimer( pScanData ps );
123
124/****************************** local functions ******************************/
125
126/** copy user-space data into kernel memory
127 */
128static 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 */
145static 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
157static unsigned long copy_from_user( pVoid dest, pVoid src, unsigned long len )
158{
159	memcpy( dest, src, len );
160	return 0;
161}
162
163static 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 */
171static 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 */
196static 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 */
217static 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 */
273static 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 */
316static 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 */
361static 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 */
382static 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 */
404static 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 */
435static 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 */
491static 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 */
584static 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 */
632static 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 */
953static 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
1186ReadFinished:
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 */
1210static 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
1236static 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
1250static int PtDrvOpen( void )
1251{
1252	if( _FALSE == PtDrvInitialized )
1253		return _E_NOT_INIT;
1254
1255	return _OK;
1256}
1257
1258static int PtDrvClose( void )
1259{
1260	if( _FALSE == PtDrvInitialized )
1261		return _E_NOT_INIT;
1262
1263	return ptdrvClose( PtDrvDevices[0] );
1264}
1265
1266static 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
1274static 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