summaryrefslogtreecommitdiff
path: root/backend/plustek-pp_ptdrv.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/plustek-pp_ptdrv.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/plustek-pp_ptdrv.c')
-rw-r--r--backend/plustek-pp_ptdrv.c1987
1 files changed, 1987 insertions, 0 deletions
diff --git a/backend/plustek-pp_ptdrv.c b/backend/plustek-pp_ptdrv.c
new file mode 100644
index 0000000..9409e56
--- /dev/null
+++ b/backend/plustek-pp_ptdrv.c
@@ -0,0 +1,1987 @@
+/* @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 <gerhard@gjaeger.de>
+ * 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 <gjheydon@bigfoot.com>)
+ * - 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
+ * .
+ * <hr>
+ * 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.
+ * <hr>
+ */
+#ifdef __KERNEL__
+# include <linux/module.h>
+# include <linux/version.h>
+
+# ifdef CONFIG_DEVFS_FS
+# include <linux/devfs_fs_kernel.h>
+# if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,69))
+# define DEVFS_26_STYLE
+# endif
+# endif
+#endif
+
+#include "plustek-pp_scan.h"
+
+#ifdef __KERNEL__
+# include <linux/param.h>
+#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 <gerhard@gjaeger.de>");
+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 ...................................................*/