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