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