/* @file plustek-pp_ptdrv.c * @brief this is the driver interface * * based on sources acquired from Plustek Inc. * Copyright (C) 1998 Plustek Inc. * Copyright (C) 2000-2013 Gerhard Jaeger * also based on the work done by Rick Bronson * * History: * - 0.30 - initial version * - 0.31 - Added some comments * - added claiming/release of parallel port resources for this driver * - added scaling function for high resolution modes where dpix < dpiy * - 0.32 - Revised lamp-off behaviour * - removed function ptdrvIsLampOn * - fixed misbehaviour when using cat /dev/pt_drv * - moved parport-functions to module misc.c * - 0.33 - added parameter lOffonEnd * - revised parport concurrency * - removed calls to ps->PositionLamp * - 0.34 - no changes * - 0.35 - removed _PTDRV_PUT_SCANNER_MODEL from ioctl interface * - added Kevins' changes (MiscRestorePort) * - added parameter legal and function PtDrvLegalRequested() * - 0.36 - removed a bug in the shutdown function * - removed all OP600P specific stuff because of the Primax tests * - added version code to ioctl interface * - added new parameter mov - model override * - removed parameter legal * - removed function PtDrvLegalRequested * - changes, due to define renaming * - patch for OpticPro 4800P * - added multiple device support * - added proc fs support/also for Kernel2.4 * - 0.37 - cleanup work, moved the procfs stuff to file procfs.c * - and some definitions to plustek_scan.h * - moved MODELSTR to misc.c * - output of the error-code after initialization * - 0.38 - added P12 stuff * - removed function ptdrvIdleMode * - moved function ptdrvP96Calibration() to p48xxCalibration * - moved function ptdrvP98Calibration() to p9636Calibration * - added devfs support (patch by Gordon Heydon ) * - 0.39 - added schedule stuff after reading one line to have a better * system response in SPP modes * - added forceMode switch * - 0.40 - added MODULE_LICENSE stuff * - 0.41 - added _PTDRV_ADJUST functionality * - changed ioctl call to PutImage * - 0.42 - added _PTDRV_SETMAP functionality * - improved the cancel functionality * - 0.43 - added LINUX_26 stuff * - changed include names * - changed version string stuff * - 0.44 - added support for more recent kernels * - fix format string issues, as Long types default to int32_t * now * . *
* This file is part of the SANE package. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * * As a special exception, the authors of SANE give permission for * additional uses of the libraries contained in this release of SANE. * * The exception is that, if you link a SANE library with other files * to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public * License. Your use of that executable is in no way restricted on * account of linking the SANE library code into it. * * This exception does not, however, invalidate any other reasons why * the executable file might be covered by the GNU General Public * License. * * If you submit changes to SANE to the maintainers to be included in * a subsequent release, you agree by submitting the changes that * those changes may be distributed with this exception intact. * * If you write modifications of your own for SANE, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. *
*/ #ifdef __KERNEL__ # include # include # ifdef CONFIG_DEVFS_FS # include # if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,69)) # define DEVFS_26_STYLE # endif # endif #endif #include "plustek-pp_scan.h" #ifdef __KERNEL__ # include #endif /****************************** static vars **********************************/ /* default port is at 0x378 */ static int port[_MAX_PTDEVS] = { 0x378, 0, 0, 0 }; #ifdef __KERNEL__ static pScanData PtDrvDevices[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = NULL}; /* default is 180 secs for lamp switch off */ static int lampoff[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 180 }; /* warmup period for lamp (30 secs) */ static int warmup[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 30 }; /* switch lamp off on unload (default = no)*/ static int lOffonEnd[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 }; /* model override (0-->none) */ static UShort mov[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 }; /* forceMode (0--> auto, 1: SPP, 2:EPP, others: auto) */ static UShort forceMode[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = 0 }; /* to use delayed I/O for each device */ static Bool slowIO[_MAX_PTDEVS] = { [0 ... (_MAX_PTDEVS-1)] = _FALSE }; #else static pScanData PtDrvDevices[_MAX_PTDEVS]= { NULL, NULL, NULL, NULL }; static int lampoff[_MAX_PTDEVS] = { 180, 180, 180, 180 }; static int warmup[_MAX_PTDEVS] = { 30, 30, 30, 30 }; static int lOffonEnd[_MAX_PTDEVS] = { 0, 0, 0, 0 }; static UShort mov[_MAX_PTDEVS] = { 0, 0, 0, 0 }; static UShort forceMode[_MAX_PTDEVS] = { 0, 0, 0, 0 }; #endif /* timers for warmup checks */ static TimerDef toTimer[_MAX_PTDEVS]; #ifndef __KERNEL__ static Bool PtDrvInitialized = _FALSE; #ifdef HAVE_SETITIMER static struct itimerval saveSettings; #endif #else static Bool deviceScanning = _FALSE; static struct timer_list tl[_MAX_PTDEVS]; /* for calculation of the timer expiration */ extern volatile unsigned long jiffies; /* the parameter interface */ #if ((LINUX_VERSION_CODE > 0x020111) && defined(MODULE)) MODULE_AUTHOR("Gerhard Jaeger "); MODULE_DESCRIPTION("Plustek parallelport-scanner driver"); /* addresses this 'new' license feature... */ #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)) MODULE_PARM(port, "1-" __MODULE_STRING(_MAX_PTDEVS) "i"); MODULE_PARM(lampoff, "1-" __MODULE_STRING(_MAX_PTDEVS) "i"); MODULE_PARM(warmup,"1-" __MODULE_STRING(_MAX_PTDEVS) "i"); MODULE_PARM(lOffonEnd, "1-" __MODULE_STRING(_MAX_PTDEVS) "i"); MODULE_PARM(mov, "1-" __MODULE_STRING(_MAX_PTDEVS) "i"); MODULE_PARM(slowIO,"1-" __MODULE_STRING(_MAX_PTDEVS) "i"); MODULE_PARM(forceMode,"1-" __MODULE_STRING(_MAX_PTDEVS) "i"); #else static int array_len = _MAX_PTDEVS; module_param_array(port, int, &array_len, 0); module_param_array(lampoff, int, &array_len, 0); module_param_array(warmup, int, &array_len, 0); module_param_array(lOffonEnd, int, &array_len, 0); module_param_array(mov, ushort, &array_len, 0); module_param_array(slowIO, int, &array_len, 0); module_param_array(forceMode, ushort, &array_len, 0); #endif MODULE_PARM_DESC(port, "I/O base address of parport"); MODULE_PARM_DESC(lampoff, "Lamp-Off timer preset in seconds"); MODULE_PARM_DESC(warmup, "Minimum warmup time in seconds"); MODULE_PARM_DESC(lOffonEnd, "1 - switchoff lamp on unload"); MODULE_PARM_DESC(mov, "Modell-override switch"); MODULE_PARM_DESC(slowIO, "0 = Fast I/O, 1 = Delayed I/O"); MODULE_PARM_DESC(forceMode, "0 = use auto detection, " "1 = use SPP mode, 2 = use EPP mode"); #endif #if defined (CONFIG_DEVFS_FS) # ifndef (DEVFS_26_STYLE) static devfs_handle_t devfs_handle = NULL; # endif #else # ifdef LINUX_26 static class_t *ptdrv_class; # endif #endif /* * the module interface */ static int pt_drv_open ( struct inode *, struct file *); static CLOSETYPE pt_drv_close( struct inode *, struct file *); #ifdef LINUX_20 static int pt_drv_read( struct inode*, struct file*, char*, int ); static int pt_drv_write( struct inode*, struct file*, const char*, int ); #else static ssize_t pt_drv_read ( struct file *file, char *buffer, size_t count, loff_t *); static ssize_t pt_drv_write( struct file *file, const char *buffer, size_t tmp,loff_t *count); #endif #ifdef NOLOCK_IOCTL static long pt_drv_ioctl( struct file *, UInt, unsigned long ); #else static int pt_drv_ioctl( struct inode *, struct file *, UInt, unsigned long ); #endif /* * the driver interface */ #ifdef LINUX_20 static struct file_operations pt_drv_fops = { NULL, /* seek */ pt_drv_read, /* read */ pt_drv_write, /* write */ NULL, /* readdir */ NULL, /* select */ pt_drv_ioctl, /* ioctl */ NULL, /* mmap */ pt_drv_open, /* open */ pt_drv_close, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ }; #else /* 2.2.x and higher stuff */ static struct file_operations pt_drv_fops = { #ifdef LINUX_24 owner: THIS_MODULE, #endif read: pt_drv_read, write: pt_drv_write, IOCTL: pt_drv_ioctl, open: pt_drv_open, release: pt_drv_close, }; #endif #endif /* guard __KERNEL */ /****************************** some prototypes ******************************/ static void ptdrvStartLampTimer( pScanData ps ); /****************************** local functions ******************************/ #ifdef __KERNEL__ /** depending on the device, return the data structure */ static pScanData get_pt_from_inode(struct inode *ip) { int minor = _MINOR(ip); /* * unit out of range */ if (minor >= _MAX_PTDEVS ) return NULL; return( PtDrvDevices[minor] ); } #endif /** copy user-space data into kernel memory */ static int getUserPtr(const pVoid useraddr, pVoid where, UInt size ) { int err = _OK; /* do parameter checks */ if((NULL == useraddr) || ( 0 == size)) return _E_INVALID; #ifdef __KERNEL__ if ((err = verify_area_20(VERIFY_READ, useraddr, size))) return err; #endif switch (size) { #ifdef __KERNEL__ case sizeof(u_char): GET_USER_RET(*(u_char *)where, (u_char *) useraddr, -EFAULT); break; case sizeof(u_short): GET_USER_RET(*(u_short *)where, (u_short *) useraddr, -EFAULT); break; case sizeof(u_long): GET_USER_RET(*(u_long *)where, (u_long *) useraddr, -EFAULT); break; default: if (copy_from_user(where, useraddr, size)) return -EFAULT; #else default: memcpy( where, useraddr, size ); #endif } return err; } /** copy kernel data into user mode address space */ static int putUserPtr( const pVoid ptr, pVoid useraddr, UInt size ) { int err = _OK; if (NULL == useraddr) return _E_INVALID; #ifdef __KERNEL__ if ((err = verify_area_20(VERIFY_WRITE, useraddr, size))) return err; if (copy_to_user(useraddr, ptr, size )) return -EFAULT; #else memcpy( useraddr, ptr, size ); #endif return err; } #ifndef __KERNEL__ static unsigned long copy_from_user( pVoid dest, pVoid src, unsigned long len ) { memcpy( dest, src, len ); return 0; } static unsigned long copy_to_user( pVoid dest, pVoid src, unsigned long len ) { memcpy( dest, src, len ); return 0; } #endif /** */ static int putUserVal(const ULong value, pVoid useraddr, UInt size) { #ifdef __KERNEL__ int err; #endif if (NULL == useraddr) return _E_INVALID; #ifdef __KERNEL__ if ((err = verify_area_20(VERIFY_WRITE, useraddr, size))) return err; #endif switch (size) { #ifdef __KERNEL__ case sizeof(u_char): PUT_USER_RET((u_char)value, (u_char *) useraddr, -EFAULT); break; case sizeof(u_short): PUT_USER_RET((u_short)value, (u_short *) useraddr, -EFAULT); break; case sizeof(u_long): PUT_USER_RET((u_long)value, (u_long *) useraddr, -EFAULT); break; #else case sizeof(UChar): *(pUChar)useraddr = (UChar)value; break; case sizeof(UShort): *(pUShort)useraddr = (UShort)value; break; case sizeof(ULong): *(pULong)useraddr = (ULong)value; break; #endif default: return _E_INVALID; } return 0; } /** switch lamp 0 on */ static void ptDrvSwitchLampOn( pScanData ps ) { DBG( DBG_LOW, "Switching lamp 0 on.\n" ); if( _IS_ASIC98(ps->sCaps.AsicID)) { ps->AsicReg.RD_ScanControl |= _SCAN_NORMALLAMP_ON; ps->bLastLampStatus = _SCAN_NORMALLAMP_ON; } else { ps->AsicReg.RD_ScanControl |= ps->bLampOn; ps->bLastLampStatus = ps->bLampOn; } IOCmdRegisterToScanner(ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl); } /** check the lamp warmup */ static void ptdrvLampWarmup( pScanData ps ) { Bool warmupNeeded; TimerDef timer; if( 0 == ps->warmup ) return; warmupNeeded = _FALSE; /* * do we have to warmup again ? Timer has not elapsed... */ if( _OK == MiscCheckTimer( &toTimer[ps->devno] )) { DBG( DBG_LOW, "Startup warmup needed!\n" ); warmupNeeded = _TRUE; } else { warmupNeeded = ps->fWarmupNeeded; } if( warmupNeeded ) { /* * correct lamp should have been switched on but * before doing anything else wait until warmup has been done */ DBG( DBG_LOW, "Waiting on warmup - %u s\n", ps->warmup ); MiscStartTimer( &timer, _SECOND * ps->warmup ); while( !MiscCheckTimer( &timer )) { /* on break, we setup the initial timer again... */ if( _FALSE == ps->fScanningStatus ) { MiscStartTimer( &toTimer[ps->devno], (_SECOND * ps->warmup)); return; } }; } #ifdef DEBUG else { DBG( DBG_LOW, "No warm-up needed \n" ); } #endif /* * start a timer here again with only a second timeout * because we need this one only for startup (Force timeout!!) */ MiscStartTimer( &toTimer[ps->devno], _SECOND ); } /** */ #ifdef __KERNEL__ static void ptdrvLampTimerIrq( unsigned long ptr ) #else static void ptdrvLampTimerIrq( int sig_num ) #endif { pScanData ps; DBG( DBG_HIGH, "!! IRQ !! Lamp-Timer stopped.\n" ); #ifdef __KERNEL__ ps = (pScanData)ptr; #else _VAR_NOT_USED( sig_num ); ps = PtDrvDevices[0]; #endif /* * paranoia check! */ if( NULL == ps ) return; if( _NO_BASE == ps->sCaps.wIOBase ) return; if( _IS_ASIC98(ps->sCaps.AsicID)) { ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMPS_ON; } else { ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMP_ON; } /* force warmup... */ ps->bLastLampStatus = 0xFF; /* * claim parallel port if necessary... * if the port is busy, restart the timer */ if( _OK != MiscClaimPort(ps)) { ptdrvStartLampTimer( ps ); return; } IOCmdRegisterToScanner( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl ); MiscReleasePort(ps); } /** */ static void ptdrvStartLampTimer( pScanData ps ) { #ifndef __KERNEL__ sigset_t block, pause_mask; struct sigaction s; #ifdef HAVE_SETITIMER struct itimerval interval; #endif /* block SIGALRM */ sigemptyset( &block ); sigaddset ( &block, SIGALRM ); sigprocmask( SIG_BLOCK, &block, &pause_mask ); /* setup handler */ sigemptyset( &s.sa_mask ); sigaddset ( &s.sa_mask, SIGINT ); s.sa_flags = 0; s.sa_handler = ptdrvLampTimerIrq; if( sigaction( SIGALRM, &s, NULL ) < 0 ) { DBG(DBG_HIGH,"pt_drv%u: Can't setup timer-irq handler\n",ps->devno); } sigprocmask( SIG_UNBLOCK, &block, &pause_mask ); #ifdef HAVE_SETITIMER /* * define a one-shot timer */ interval.it_value.tv_usec = 0; interval.it_value.tv_sec = ps->lampoff; interval.it_interval.tv_usec = 0; interval.it_interval.tv_sec = 0; if( 0 != ps->lampoff ) setitimer( ITIMER_REAL, &interval, &saveSettings ); #else alarm( ps->lampoff ); #endif #else init_timer( &tl[ps->devno] ); /* timeout val in seconds */ tl[ps->devno].expires = jiffies + ps->lampoff * HZ; tl[ps->devno].data = (unsigned long)ps; tl[ps->devno].function = ptdrvLampTimerIrq; if( 0 != ps->lampoff ) add_timer( &tl[ps->devno] ); #endif DBG( DBG_HIGH, "Lamp-Timer started!\n" ); } /** */ static void ptdrvStopLampTimer( pScanData ps ) { #ifndef __KERNEL__ sigset_t block, pause_mask; /* block SIGALRM */ sigemptyset( &block ); sigaddset ( &block, SIGALRM ); sigprocmask( SIG_BLOCK, &block, &pause_mask ); #ifdef HAVE_SETITIMER if( 0 != ps->lampoff ) setitimer( ITIMER_REAL, &saveSettings, NULL ); #else _VAR_NOT_USED( ps ); alarm(0); #endif #else if( 0 != ps->lampoff ) del_timer( &tl[ps->devno] ); #endif DBG( DBG_HIGH, "Lamp-Timer stopped!\n" ); } /** claim and initialize the requested port */ static int ptdrvOpen( pScanData ps, int portBase ) { int retval; DBG( DBG_HIGH, "ptdrvOpen(port=0x%x)\n", (int32_t)portBase ); if( NULL == ps ) return _E_NULLPTR; /* * claim port resources... */ retval = MiscClaimPort(ps); if( _OK != retval ) return retval; return MiscInitPorts( ps, portBase ); } /** free used memory (if necessary) * restore the parallel port settings and release the port */ static int ptdrvClose( pScanData ps ) { DBG( DBG_HIGH, "ptdrvClose()\n" ); if( NULL == ps ) return _E_NULLPTR; /* * should be cleared by ioctl(close) */ if ( NULL != ps->driverbuf ) { DBG( DBG_LOW, "*** cleanup buffers ***\n" ); _VFREE( ps->driverbuf ); ps->driverbuf = NULL; } if ( NULL != ps->Shade.pHilight ) { _VFREE( ps->Shade.pHilight ); ps->Shade.pHilight = NULL; } /* * restore/release port resources... */ MiscRestorePort( ps ); MiscReleasePort( ps ); return _OK; } /** will be called during OPEN_DEVICE ioctl call */ static int ptdrvOpenDevice( pScanData ps ) { int retval, iobase; UShort asic; UChar lastStat; UShort lastMode; ULong devno; #ifdef __KERNEL__ UShort flags; struct pardevice *pd; struct parport *pp; ProcDirDef procDir; #else int pd; #endif /* * push some values from the struct */ #ifdef __KERNEL__ flags = ps->flags; pp = ps->pp; procDir = ps->procDir; #endif pd = ps->pardev; iobase = ps->sCaps.wIOBase; asic = ps->sCaps.AsicID; lastStat = ps->bLastLampStatus; lastMode = ps->IO.lastPortMode; devno = ps->devno; /* * reinit the show */ ptdrvStopLampTimer( ps ); MiscReinitStruct ( ps ); /* * pop the val(s) */ #ifdef __KERNEL__ ps->flags = flags; ps->pp = pp; ps->procDir = procDir; #endif ps->pardev = pd; ps->bLastLampStatus = lastStat; ps->IO.lastPortMode = lastMode; ps->devno = devno; #ifdef __KERNEL__ if( _TRUE == slowIO[devno] ) { DBG( DBG_LOW, "Using slow I/O\n" ); ps->IO.slowIO = _TRUE; ps->IO.fnOut = IOOutDelayed; ps->IO.fnIn = IOInDelayed; } else { DBG( DBG_LOW, "Using fast I/O\n" ); ps->IO.slowIO = _FALSE; ps->IO.fnOut = IOOut; ps->IO.fnIn = IOIn; } #endif ps->ModelOverride = mov[devno]; ps->warmup = warmup[devno]; ps->lampoff = lampoff[devno]; ps->lOffonEnd = lOffonEnd[devno]; ps->IO.forceMode = forceMode[devno]; /* * try to find scanner again */ retval = ptdrvOpen( ps, iobase ); if( _OK == retval ) retval = DetectScanner( ps, asic ); else ptdrvStartLampTimer( ps ); return retval; } /*............................................................................. * initialize the driver * allocate memory for the ScanData structure and do some presets */ static int ptdrvInit( int devno ) { int retval; pScanData ps; DBG( DBG_HIGH, "ptdrvInit(%u)\n", devno ); if( devno >= _MAX_PTDEVS ) return _E_NO_DEV; /* * allocate memory for our large ScanData-structure */ ps = MiscAllocAndInitStruct(); if( NULL == ps ) { return _E_ALLOC; } #ifdef __KERNEL__ if( _TRUE == slowIO[devno] ) { DBG( DBG_LOW, "Using slow I/O\n" ); ps->IO.slowIO = _TRUE; ps->IO.fnOut = IOOutDelayed; ps->IO.fnIn = IOInDelayed; } else { DBG( DBG_LOW, "Using fast I/O\n" ); ps->IO.slowIO = _FALSE; ps->IO.fnOut = IOOut; ps->IO.fnIn = IOIn; } #endif ps->ModelOverride = mov[devno]; ps->warmup = warmup[devno]; ps->lampoff = lampoff[devno]; ps->lOffonEnd = lOffonEnd[devno]; ps->IO.forceMode = forceMode[devno]; ps->devno = devno; /* assign it right here, to allow correct shutdown */ PtDrvDevices[devno] = ps; /* * try to register the port */ retval = MiscRegisterPort( ps, port[devno] ); if( _OK == retval ) { retval = ptdrvOpen( ps, port[devno] ); } /* * try to detect a scanner... */ if( _OK == retval ) { retval = DetectScanner( ps, 0 ); /* do this here before releasing the port */ if( _OK == retval ) { ptDrvSwitchLampOn( ps ); } ptdrvClose( ps ); } if( _OK == retval ) { #ifdef __KERNEL__ _PRINT( "pt_drv%u: %s found on port 0x%04x\n", devno, MiscGetModelName(ps->sCaps.Model), ps->IO.pbSppDataPort ); #else DBG( DBG_LOW, "pt_drv%u: %s found\n", devno, MiscGetModelName(ps->sCaps.Model)); #endif /* * initialize the timespan timer */ MiscStartTimer( &toTimer[ps->devno], (_SECOND * ps->warmup)); if( 0 == ps->lampoff ) #ifdef __KERNEL__ _PRINT( #else DBG( DBG_LOW, #endif "pt_drv%u: Lamp-Timer switched off.\n", devno ); else { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_LOW, #endif "pt_drv%u: Lamp-Timer set to %u seconds.\n", devno, ps->lampoff ); } #ifdef __KERNEL__ _PRINT( #else DBG( DBG_LOW, #endif "pt_drv%u: WarmUp period set to %u seconds.\n", devno, ps->warmup ); if( 0 == ps->lOffonEnd ) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_LOW, #endif "pt_drv%u: Lamp untouched on driver unload.\n", devno ); } else { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_LOW, #endif "pt_drv%u: Lamp switch-off on driver unload.\n", devno ); } ptdrvStartLampTimer( ps ); } return retval; } /*............................................................................. * shutdown the driver: * switch the lights out * stop the motor * free memory */ static int ptdrvShutdown( pScanData ps ) { int devno; DBG( DBG_HIGH, "ptdrvShutdown()\n" ); if( NULL == ps ) return _E_NULLPTR; devno = ps->devno; DBG( DBG_HIGH, "cleanup device %u\n", devno ); if( _NO_BASE != ps->sCaps.wIOBase ) { ptdrvStopLampTimer( ps ); if( _OK == MiscClaimPort(ps)) { ps->PutToIdleMode( ps ); if( 0 != ps->lOffonEnd ) { if( _IS_ASIC98(ps->sCaps.AsicID)) { ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMPS_ON; } else { ps->AsicReg.RD_ScanControl &= ~_SCAN_LAMP_ON; } IOCmdRegisterToScanner( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl ); } } MiscReleasePort( ps ); } /* unregister the driver */ MiscUnregisterPort( ps ); _KFREE( ps ); if( devno < _MAX_PTDEVS ) PtDrvDevices[devno] = NULL; return _OK; } /*............................................................................. * the IOCTL interface */ static int ptdrvIoctl( pScanData ps, UInt cmd, pVoid arg ) { UShort dir; UShort version; UInt size; ULong argVal; int cancel; int retval; /* * do the preliminary stuff here */ if( NULL == ps ) return _E_NULLPTR; retval = _OK; dir = _IOC_DIR(cmd); size = _IOC_SIZE(cmd); if ((_IOC_WRITE == dir) && size && (size <= sizeof(ULong))) { if (( retval = getUserPtr( arg, &argVal, size))) { DBG( DBG_HIGH, "ioctl() failed - result = %i\n", retval ); return retval; } } switch( cmd ) { /* open */ case _PTDRV_OPEN_DEVICE: DBG( DBG_LOW, "ioctl(_PTDRV_OPEN_DEVICE)\n" ); if (copy_from_user(&version, arg, sizeof(UShort))) return _E_FAULT; if( _PTDRV_IOCTL_VERSION != version ) { DBG( DBG_HIGH, "Version mismatch: Backend=0x%04X(0x%04X)", version, _PTDRV_IOCTL_VERSION ); return _E_VERSION; } retval = ptdrvOpenDevice( ps ); break; /* close */ case _PTDRV_CLOSE_DEVICE: DBG( DBG_LOW, "ioctl(_PTDRV_CLOSE_DEVICE)\n" ); if ( NULL != ps->driverbuf ) { DBG( DBG_LOW, "*** cleanup buffers ***\n" ); _VFREE( ps->driverbuf ); ps->driverbuf = NULL; } if ( NULL != ps->Shade.pHilight ) { _VFREE( ps->Shade.pHilight ); ps->Shade.pHilight = NULL; } ps->PutToIdleMode( ps ); ptdrvStartLampTimer( ps ); break; /* get caps - no scanner connection necessary */ case _PTDRV_GET_CAPABILITIES: DBG( DBG_LOW, "ioctl(_PTDRV_GET_CAPABILITES)\n" ); return putUserPtr( &ps->sCaps, arg, size); break; /* get lens-info - no scanner connection necessary */ case _PTDRV_GET_LENSINFO: DBG( DBG_LOW, "ioctl(_PTDRV_GET_LENSINFO)\n" ); return putUserPtr( &ps->LensInf, arg, size); break; /* put the image info - no scanner connection necessary */ case _PTDRV_PUT_IMAGEINFO: { short tmpcx, tmpcy; ImgDef img; DBG( DBG_LOW, "ioctl(_PTDRV_PUT_IMAGEINFO)\n" ); if (copy_from_user( &img, (pImgDef)arg, size)) return _E_FAULT; tmpcx = (short)img.crArea.cx; tmpcy = (short)img.crArea.cy; if(( 0 >= tmpcx ) || ( 0 >= tmpcy )) { DBG( DBG_LOW, "CX or CY <= 0!!\n" ); return _E_INVALID; } _ASSERT( ps->GetImageInfo ); ps->GetImageInfo( ps, &img ); } break; /* get crop area - no scanner connection necessary */ case _PTDRV_GET_CROPINFO: { CropInfo outBuffer; pCropInfo pcInf = &outBuffer; DBG( DBG_LOW, "ioctl(_PTDRV_GET_CROPINFO)\n" ); memset( pcInf, 0, sizeof(CropInfo)); pcInf->dwPixelsPerLine = ps->DataInf.dwAppPixelsPerLine; pcInf->dwBytesPerLine = ps->DataInf.dwAppBytesPerLine; pcInf->dwLinesPerArea = ps->DataInf.dwAppLinesPerArea; return putUserPtr( pcInf, arg, size ); } break; /* adjust the driver settings */ case _PTDRV_ADJUST: { PPAdjDef adj; DBG( DBG_LOW, "ioctl(_PTDRV_ADJUST)\n" ); if (copy_from_user(&adj, (pPPAdjDef)arg, sizeof(PPAdjDef))) return _E_FAULT; DBG( DBG_LOW, "Adjusting device %u\n", ps->devno ); DBG( DBG_LOW, "warmup: %i\n", adj.warmup ); DBG( DBG_LOW, "lampOff: %i\n", adj.lampOff ); DBG( DBG_LOW, "lampOffOnEnd: %i\n", adj.lampOffOnEnd ); if( ps->devno < _MAX_PTDEVS ) { if( adj.warmup >= 0 ) { warmup[ps->devno] = adj.warmup; ps->warmup = adj.warmup; } if( adj.lampOff >= 0 ) { lampoff[ps->devno] = adj.lampOff; ps->lampoff = adj.lampOff; } if( adj.lampOffOnEnd >= 0 ) { lOffonEnd[ps->devno] = adj.lampOffOnEnd; ps->lOffonEnd = adj.lampOffOnEnd; } } } break; /* set a specific map (r,g,b or gray) */ case _PTDRV_SETMAP: { int i, x_len; MapDef map; DBG( DBG_LOW, "ioctl(_PTDRV_SETMAP)\n" ); if (copy_from_user( &map, (pMapDef)arg, sizeof(MapDef))) return _E_FAULT; DBG( DBG_LOW, "maplen=%u, mapid=%u, addr=0x%08lx\n", map.len, map.map_id, (u_long)map.map ); x_len = 256; if( _IS_ASIC98(ps->sCaps.AsicID)) x_len = 4096; /* check for 0 pointer and len */ if((NULL == map.map) || (x_len != map.len)) { DBG( DBG_LOW, "map pointer == 0, or map len invalid!!\n" ); return _E_INVALID; } if( _MAP_MASTER == map.map_id ) { for( i = 0; i < 3; i++ ) { if (copy_from_user((pVoid)&ps->a_bMapTable[x_len * i], map.map, x_len )) { return _E_FAULT; } } } else { u_long idx = 0; if( map.map_id == _MAP_GREEN ) idx = 1; if( map.map_id == _MAP_BLUE ) idx = 2; if (copy_from_user((pVoid)&ps->a_bMapTable[x_len * idx], map.map, x_len )) { return _E_FAULT; } } /* here we adjust the maps according to * the brightness and contrast settings */ MapAdjust( ps, map.map_id ); } break; /* set environment - no scanner connection necessary */ case _PTDRV_SET_ENV: { ScanInfo sInf; DBG( DBG_LOW, "ioctl(_PTDRV_SET_ENV)\n" ); if (copy_from_user(&sInf, (pScanInfo)arg, sizeof(ScanInfo))) return _E_FAULT; /* * to make the OpticPro 4800P work, we need to invert the * Inverse flag */ if( _ASIC_IS_96001 == ps->sCaps.AsicID ) { if( SCANDEF_Inverse & sInf.ImgDef.dwFlag ) sInf.ImgDef.dwFlag &= ~SCANDEF_Inverse; else sInf.ImgDef.dwFlag |= SCANDEF_Inverse; } _ASSERT( ps->SetupScanSettings ); retval = ps->SetupScanSettings( ps, &sInf ); /* CHANGE preset map here */ if( _OK == retval ) { MapInitialize ( ps ); MapSetupDither( ps ); ps->DataInf.dwVxdFlag |= _VF_ENVIRONMENT_READY; if (copy_to_user((pScanInfo)arg, &sInf, sizeof(ScanInfo))) return _E_FAULT; } } break; /* start scan */ case _PTDRV_START_SCAN: { StartScan outBuffer; pStartScan pstart = (pStartScan)&outBuffer; DBG( DBG_LOW, "ioctl(_PTDRV_START_SCAN)\n" ); retval = IOIsReadyForScan( ps ); if( _OK == retval ) { ps->dwDitherIndex = 0; ps->fScanningStatus = _TRUE; pstart->dwBytesPerLine = ps->DataInf.dwAppBytesPerLine; pstart->dwLinesPerScan = ps->DataInf.dwAppLinesPerArea; pstart->dwFlag = ps->DataInf.dwScanFlag; ps->DataInf.dwVxdFlag |= _VF_FIRSTSCANLINE; ps->DataInf.dwScanFlag&=~(_SCANNER_SCANNING|_SCANNER_PAPEROUT); if (copy_to_user((pStartScan)arg, pstart, sizeof(StartScan))) return _E_FAULT; } } break; /* stop scan */ case _PTDRV_STOP_SCAN: DBG( DBG_LOW, "ioctl(_PTDRV_STOP_SCAN)\n" ); if (copy_from_user(&cancel, arg, sizeof(short))) return _E_FAULT; /* we may use this to abort scanning! */ ps->fScanningStatus = _FALSE; /* when using this to cancel, then that's all */ if( _FALSE == cancel ) { MotorToHomePosition( ps ); ps->DataInf.dwAppLinesPerArea = 0; ps->DataInf.dwScanFlag &= ~_SCANNER_SCANNING; /* if environment was never set */ if (!(ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY)) retval = _E_SEQUENCE; ps->DataInf.dwVxdFlag &= ~_VF_ENVIRONMENT_READY; } else { DBG( DBG_LOW, "CANCEL Mode set\n" ); } retval = putUserVal(retval, arg, size); break; /* read the flag status register, when reading the action button, you must * only do this call and none of the other ioctl's * like open, etc or it will always show up as "1" */ case _PTDRV_ACTION_BUTTON: DBG( DBG_LOW, "ioctl(_PTDRV_ACTION_BUTTON)\n" ); IODataRegisterFromScanner( ps, ps->RegStatus ); retval = putUserVal( argVal, arg, size ); break; default: retval = _E_NOSUPP; break; } return retval; } /*............................................................................. * read the data */ static int ptdrvRead( pScanData ps, pUChar buffer, int count ) { pUChar scaleBuf; ULong dwLinesRead = 0; int retval = _OK; #ifdef _ASIC_98001_SIM #ifdef __KERNEL__ _PRINT( #else DBG( DBG_LOW, #endif "pt_drv : Software-Emulation active, can't read!\n" ); return _E_INVALID; #endif if((NULL == buffer) || (NULL == ps)) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv : Internal NULL-pointer!\n" ); return _E_NULLPTR; } if( 0 == count ) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv%u: reading 0 bytes makes no sense!\n", ps->devno ); return _E_INVALID; } if( _FALSE == ps->fScanningStatus ) return _E_ABORT; /* * has the environment been set ? * this should prevent the driver from causing a seg-fault * when using the cat /dev/pt_drv command! */ if (!(ps->DataInf.dwVxdFlag & _VF_ENVIRONMENT_READY)) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv%u: Cannot read, driver not initialized!\n",ps->devno); return _E_SEQUENCE; } /* * get some memory */ ps->Scan.bp.pMonoBuf = _KALLOC( ps->DataInf.dwAppPhyBytesPerLine, GFP_KERNEL); if ( NULL == ps->Scan.bp.pMonoBuf ) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv%u: Not enough memory available!\n", ps->devno ); return _E_ALLOC; } /* if we have to do some scaling, we need another buffer... */ if( ps->DataInf.XYRatio > 1000 ) { scaleBuf = _KALLOC( ps->DataInf.dwAppPhyBytesPerLine, GFP_KERNEL); if ( NULL == scaleBuf ) { _KFREE( ps->Scan.bp.pMonoBuf ); #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv%u: Not enough memory available!\n", ps->devno ); return _E_ALLOC; } } else { scaleBuf = NULL; } DBG( DBG_LOW, "PtDrvRead(%u bytes)*****************\n", count ); DBG( DBG_LOW, "MonoBuf = 0x%08lx[%u], scaleBuf = 0x%lx\n", (unsigned long)ps->Scan.bp.pMonoBuf, ps->DataInf.dwAppPhyBytesPerLine, (unsigned long)scaleBuf ); /* * in case of a previous problem, move the sensor back home */ MotorToHomePosition( ps ); if( _FALSE == ps->fScanningStatus ) { retval = _E_ABORT; goto ReadFinished; } dwLinesRead = 0; /* * first of all calibrate the show */ ps->bMoveDataOutFlag = _DataInNormalState; ps->fHalfStepTableFlag = _FALSE; ps->fReshaded = _FALSE; ps->fScanningStatus = _TRUE; if( _ASIC_IS_98003 == ps->sCaps.AsicID ) ps->Scan.fRefreshState = _FALSE; else ps->Scan.fRefreshState = _TRUE; ptdrvLampWarmup( ps ); if( _FALSE == ps->fScanningStatus ) { retval = _E_ABORT; goto ReadFinished; } retval = ps->Calibration( ps ); if( _OK != retval ) { #ifdef __KERNEL__ _PRINT( #else DBG( DBG_HIGH, #endif "pt_drv%u: calibration failed, result = %i\n", ps->devno, retval ); goto ReadFinished; } if( _ASIC_IS_98003 == ps->sCaps.AsicID ) { ps->OpenScanPath( ps ); MotorP98003ForceToLeaveHomePos( ps ); } _ASSERT(ps->SetupScanningCondition); ps->SetupScanningCondition(ps); if( _ASIC_IS_98003 != ps->sCaps.AsicID ) { ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE ); IOSetToMotorRegister( ps ); } else { ps->WaitForPositionY( ps ); _DODELAY( 70 ); ps->Scan.bOldScanState = IOGetScanState( ps, _TRUE ) & _SCANSTATE_MASK; } ps->DataInf.dwScanFlag |= _SCANNER_SCANNING; if( _FALSE == ps->fScanningStatus ) { DBG( DBG_HIGH, "read aborted!\n" ); retval = _E_ABORT; goto ReadFinished; } /* * now get the picture data */ DBG( DBG_HIGH, "dwAppLinesPerArea = %d\n", ps->DataInf.dwAppLinesPerArea); DBG( DBG_HIGH, "dwAppBytesPerLine = %d\n", ps->DataInf.dwAppBytesPerLine); /* HEINER: A3I ps->bMoveDataOutFlag = _DataFromStopState; */ if ( 0 != ps->DataInf.dwAppLinesPerArea ) { ps->Scan.dwLinesToRead = count / ps->DataInf.dwAppBytesPerLine; if( ps->Scan.dwLinesToRead ) { DBG( DBG_HIGH, "dwLinesToRead = %d\n", ps->Scan.dwLinesToRead ); if( ps->Scan.dwLinesToRead > ps->DataInf.dwAppLinesPerArea ) ps->Scan.dwLinesToRead = ps->DataInf.dwAppLinesPerArea; ps->DataInf.dwAppLinesPerArea -= ps->Scan.dwLinesToRead; if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle) buffer += ((ps->Scan.dwLinesToRead - 1) * ps->DataInf.dwAppBytesPerLine); if (ps->DataInf.dwVxdFlag & _VF_DATATOUSERBUFFER) ps->DataInf.pCurrentBuffer = ps->Scan.bp.pMonoBuf; while(ps->fScanningStatus && ps->Scan.dwLinesToRead) { _ASSERT(ps->ReadOneImageLine); if (!ps->ReadOneImageLine(ps)) { ps->fScanningStatus = _FALSE; DBG( DBG_HIGH, "ReadOneImageLine() failed at line %u!\n", dwLinesRead ); break; } /* * as we might scan images that exceed the CCD-capabilities * in x-resolution, we have to enlarge the line data * i.e.: scanning at 1200dpi generates on a P9636 600 dpi in * x-direction but 1200dpi in y-direction... */ if( NULL != scaleBuf ) { ScaleX( ps, ps->Scan.bp.pMonoBuf, scaleBuf ); if (copy_to_user( buffer, scaleBuf, ps->DataInf.dwAppPhyBytesPerLine)) { return _E_FAULT; } } else { if (copy_to_user( buffer, ps->Scan.bp.pMonoBuf, ps->DataInf.dwAppPhyBytesPerLine)) { return _E_FAULT; } } buffer += ps->Scan.lBufferAdjust; dwLinesRead++; ps->Scan.dwLinesToRead--; /* needed, esp. to avoid freezing the system in SPP mode */ #ifdef __KERNEL__ schedule(); /*#else sched_yield(); */ #endif } if (ps->fScanningStatus) { if( _IS_ASIC96(ps->sCaps.AsicID)) MotorP96SetSpeedToStopProc(ps); } else { if (ps->DataInf.dwScanFlag & (SCANDEF_StopWhenPaperOut | SCANDEF_UnlimitLength)) { ps->DataInf.dwAppLinesPerArea = 0; } else { if (ps->DataInf.dwScanFlag & SCANDEF_BmpStyle) buffer -= (ps->DataInf.dwAppBytesPerLine * (ps->Scan.dwLinesToRead - 1)); memset( buffer, 0xff, ps->Scan.dwLinesToRead * ps->DataInf.dwAppBytesPerLine ); dwLinesRead += ps->Scan.dwLinesToRead; } } } else { retval = _E_INTERNAL; } } if( _FALSE == ps->fScanningStatus ) { DBG( DBG_HIGH, "read aborted!\n" ); retval = _E_ABORT; } ReadFinished: if( _ASIC_IS_98003 == ps->sCaps.AsicID ) ps->CloseScanPath( ps ); if( NULL != ps->Scan.bp.pMonoBuf ) _KFREE( ps->Scan.bp.pMonoBuf ); if( NULL != scaleBuf ) _KFREE( scaleBuf ); /* * on success return number of bytes red */ if ( _OK == retval ) return (ps->DataInf.dwAppPhyBytesPerLine * dwLinesRead); return retval; } /*************************** the module interface ****************************/ #ifdef __KERNEL__ /* the kernel module interface */ /* Designed to be used as a module */ #ifdef MODULE /*............................................................................. * gets called upon module initialization */ #ifdef LINUX_26 static int __init ptdrv_init( void ) #else int init_module( void ) #endif { UInt devCount; UInt i; int retval = _OK; int result = _OK; #if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE)) char controlname[24]; #endif # ifdef LINUX_26 char devname[20]; #endif DBG( DBG_HIGH, "*********************************************\n" ); DBG( DBG_HIGH, "pt_drv: init_module()\n" ); #if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE)) devfs_handle = devfs_mk_dir(NULL, "scanner", NULL); if( devfs_register_chrdev(_PTDRV_MAJOR, _DRV_NAME, &pt_drv_fops)) { #else if( register_chrdev(_PTDRV_MAJOR, _DRV_NAME, &pt_drv_fops)) { #endif _PRINT(KERN_INFO "pt_drv: unable to get major %d for pt_drv devices\n", _PTDRV_MAJOR); return -EIO; } printk( KERN_INFO "pt_drv : driver version "_PTDRV_VERSTR"\n" ); #if !defined (CONFIG_DEVFS_FS) && defined (LINUX_26) ptdrv_class = class_create(THIS_MODULE, "scanner"); if (IS_ERR(ptdrv_class)) goto out_devfs; #endif /* register the proc_fs */ ProcFsInitialize(); /* go through the list of defined ports and try to find a device */ devCount = 0; for( i = 0; i < _MAX_PTDEVS; i++ ) { if( 0 != port[i] ) { result = ptdrvInit( i ); if ( _OK == result ) { PtDrvDevices[i]->flags |= _PTDRV_INITALIZED; #ifdef CONFIG_DEVFS_FS # ifndef DEVFS_26_STYLE sprintf( controlname, "scanner/pt_drv%d", devCount ); devfs_register( NULL, controlname, DEVFS_FL_DEFAULT, _PTDRV_MAJOR, 0, (S_IFCHR | S_IRUGO | S_IWUGO | S_IFCHR), &pt_drv_fops, NULL ); # else /* DEVFS_26_STYLE */ devfs_mk_cdev(MKDEV(_PTDRV_MAJOR, devCount), (S_IFCHR | S_IRUGO | S_IWUGO | S_IFCHR), "scanner/pt_drv%d", devCount); # endif #else # ifdef LINUX_26 sprintf(devname, "pt_drv%d", devCount); CLASS_DEV_CREATE(ptdrv_class, MKDEV(_PTDRV_MAJOR, devCount), NULL, devname); # endif /* LINUX_26 */ #endif /* CONFIG_DEVFS_FS */ ProcFsRegisterDevice( PtDrvDevices[i] ); devCount++; } else { retval = result; ptdrvShutdown( PtDrvDevices[i] ); PtDrvDevices[i] = NULL; } } } /* * if something went wrong, shutdown all... */ if( devCount == 0 ) { #if !defined (CONFIG_DEVFS_FS) && defined (LINUX_26) out_devfs: class_destroy(ptdrv_class); #endif #if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE)) devfs_unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME ); #else unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME ); #endif ProcFsShutdown(); #ifdef __KERNEL__ _PRINT( KERN_INFO "pt_drv : no device(s) detected, (%i)\n", retval ); #endif } else { DBG( DBG_HIGH, "pt_drv : init done, %u device(s) found\n", devCount ); retval = _OK; } DBG( DBG_HIGH, "---------------------------------------------\n" ); deviceScanning = _FALSE; return retval; } /*............................................................................. * cleanup the show */ #ifdef LINUX_26 static void __exit ptdrv_exit( void ) #else void cleanup_module( void ) #endif { UInt i; pScanData ps; #if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE)) char controlname[24]; devfs_handle_t master; #endif DBG( DBG_HIGH, "pt_drv: cleanup_module()\n" ); for ( i = 0; i < _MAX_PTDEVS; i++ ) { ps = PtDrvDevices[i]; PtDrvDevices[i] = NULL; if ( NULL != ps ) { #ifdef CONFIG_DEVFS_FS # ifndef DEVFS_26_STYLE sprintf( controlname, "scanner/pt_drv%d", i ); master = devfs_find_handle( NULL,controlname, 0, 0, DEVFS_SPECIAL_CHR, 0 ); devfs_unregister( master ); # else devfs_remove("scanner/pt_drv%d", i); # endif #else # ifdef LINUX_26 CLASS_DEV_DESTROY(ptdrv_class, MKDEV(_PTDRV_MAJOR, i)); # endif /* LINUX_26 */ #endif /* CONFIG_DEVFS_FS */ ptdrvShutdown( ps ); ProcFsUnregisterDevice( ps ); } } #if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE)) devfs_unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME ); #else unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME ); #endif ProcFsShutdown(); #if !defined (CONFIG_DEVFS_FS) && defined (LINUX_26) class_destroy(ptdrv_class); #endif DBG( DBG_HIGH, "pt_drv: cleanup done.\n" ); DBG( DBG_HIGH, "*********************************************\n" ); } #ifdef LINUX_26 module_init(ptdrv_init); module_exit(ptdrv_exit); #endif #endif /*MODULE*/ /*............................................................................. * device open... */ static int pt_drv_open(struct inode *inode, struct file *file) { pScanData ps; DBG( DBG_HIGH, "pt_drv_open()\n" ); ps = get_pt_from_inode(inode); if ( NULL == ps ) { return(-ENXIO); } /* device not found ? */ if (!(ps->flags & _PTDRV_INITALIZED)) { return(-ENXIO); } /* device is busy ? */ if (ps->flags & _PTDRV_OPEN) { return(-EBUSY); } #ifdef LINUX_26 if (!try_module_get(THIS_MODULE)) return -EAGAIN; #else MOD_INC_USE_COUNT; #endif ps->flags |= _PTDRV_OPEN; return _OK; } /*............................................................................. * device close... */ static CLOSETYPE pt_drv_close(struct inode * inode, struct file * file) { pScanData ps; DBG( DBG_HIGH, "pt_drv_close()\n" ); if ((ps = get_pt_from_inode(inode)) ) { ptdrvClose( ps ); ps->flags &= ~_PTDRV_OPEN; #ifdef LINUX_26 module_put(THIS_MODULE); #else MOD_DEC_USE_COUNT; #endif CLOSERETURN(0); } else { DBG( DBG_HIGH, "pt_drv: - close failed!\n" ); CLOSERETURN(-ENXIO); } } /*............................................................................. * read data from device */ #ifdef LINUX_20 static int pt_drv_read(struct inode *inode, struct file *file, char *buffer, int count) { int result; pScanData ps; if ( !(ps = get_pt_from_inode(inode))) return(-ENXIO); #else static ssize_t pt_drv_read( struct file *file, char *buffer, size_t count, loff_t *tmp ) { int result; pScanData ps; if ( !(ps = get_pt_from_inode(file->f_dentry->d_inode)) ) return(-ENXIO); #endif if ((result = verify_area_20(VERIFY_WRITE, buffer, count))) return result; /* * as the driver contains some global vars, it is not * possible to scan simultaenously with two or more devices */ if( _TRUE == deviceScanning ) { printk( KERN_INFO "pt_drv: device %u busy!!!\n", ps->devno ); return(-EBUSY); } deviceScanning = _TRUE; result = ptdrvRead( ps, buffer, count ); deviceScanning = _FALSE; return result; } /*............................................................................. * writing makes no sense */ #ifdef LINUX_20 static int pt_drv_write(struct inode * inode, struct file * file, const char * buffer, int count) { return -EPERM; } #else static ssize_t pt_drv_write( struct file * file,const char * buffer, size_t tmp,loff_t* count) { return -EPERM; } #endif /*............................................................................. * the ioctl interface */ #ifdef NOLOCK_IOCTL static long pt_drv_ioctl( struct file *file, UInt cmd, unsigned long arg ) { pScanData ps; if ( !(ps = get_pt_from_inode(file->f_dentry->d_inode)) ) return(-ENXIO); return ptdrvIoctl( ps, cmd, (pVoid)arg); } #else static int pt_drv_ioctl( struct inode *inode, struct file *file, UInt cmd, unsigned long arg ) { pScanData ps; if ( !(ps = get_pt_from_inode(inode)) ) return(-ENXIO); return ptdrvIoctl( ps, cmd, (pVoid)arg); } #endif #else /* the user-mode interface */ /*............................................................................. * here we only have wrapper functions */ static int PtDrvInit( const char *dev_name, UShort model_override ) { int fd; int result = _OK; if( _TRUE == PtDrvInitialized ) return _OK; result = sanei_pp_open( dev_name, &fd ); if( SANE_STATUS_GOOD != result ) return result; port[0] = fd; mov[0] = model_override; result = ptdrvInit( 0 ); if( _OK == result ) { PtDrvInitialized = _TRUE; } else { ptdrvShutdown( PtDrvDevices[0] ); } return result; } static int PtDrvShutdown( void ) { int result; if( _FALSE == PtDrvInitialized ) return _E_NOT_INIT; result = ptdrvShutdown( PtDrvDevices[0] ); PtDrvInitialized = _FALSE; return result; } static int PtDrvOpen( void ) { if( _FALSE == PtDrvInitialized ) return _E_NOT_INIT; return _OK; } static int PtDrvClose( void ) { if( _FALSE == PtDrvInitialized ) return _E_NOT_INIT; return ptdrvClose( PtDrvDevices[0] ); } static int PtDrvIoctl( UInt cmd, pVoid arg ) { if( _FALSE == PtDrvInitialized ) return _E_NOT_INIT; return ptdrvIoctl( PtDrvDevices[0], cmd, arg); } static int PtDrvRead ( pUChar buffer, int count ) { if( _FALSE == PtDrvInitialized ) return _E_NOT_INIT; return ptdrvRead( PtDrvDevices[0], buffer, count ); } #endif /* guard __KERNEL__ */ /* END PLUSTEK-PP_PTDRV.C ...................................................*/