kopia lustrzana https://gitlab.com/sane-project/backends
1906 wiersze
44 KiB
C
1906 wiersze
44 KiB
C
/*.............................................................................
|
|
* Project : SANE library for Plustek parallelport flatbed scanners.
|
|
*.............................................................................
|
|
*/
|
|
|
|
/* @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-2003 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
|
|
* - added LINUX_26 stuff
|
|
* - changed include names
|
|
* .
|
|
* <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>
|
|
|
|
# ifdef CONFIG_DEVFS_FS
|
|
# include <linux/devfs_fs_kernel.h>
|
|
# 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 ULong 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
|
|
|
|
MODULE_PARM(port, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(port, "I/O base address of parport");
|
|
|
|
MODULE_PARM(lampoff, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(lampoff, "Lamp-Off timer preset in seconds");
|
|
|
|
MODULE_PARM(warmup,"1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(warmup, "Minimum warmup time in seconds");
|
|
|
|
MODULE_PARM(lOffonEnd, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(lOffonEnd, "1 - switchoff lamp on unload");
|
|
|
|
MODULE_PARM(mov, "1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(mov, "Modell-override switch");
|
|
|
|
MODULE_PARM(slowIO,"1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(slowIO, "0 = Fast I/O, 1 = Delayed I/O");
|
|
|
|
MODULE_PARM(forceMode,"1-" __MODULE_STRING(_MAX_PTDEVS) "i");
|
|
MODULE_PARM_DESC(forceMode, "0 = use auto detection, "
|
|
"1 = use SPP mode, 2 = use EPP mode");
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEVFS_FS
|
|
static devfs_handle_t devfs_handle = NULL;
|
|
#endif
|
|
|
|
/*
|
|
* the module interface
|
|
*/
|
|
static int pt_drv_open ( struct inode *, struct file *);
|
|
static CLOSETYPE pt_drv_close( struct inode *, struct file *);
|
|
static int pt_drv_ioctl( struct inode *, struct file *, UInt, ULong );
|
|
|
|
#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
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
#ifdef LINUX_26
|
|
int minor = MINOR(kdev_t_to_nr(ip->i_rdev));
|
|
#else
|
|
int minor = MINOR(ip->i_rdev);
|
|
#endif
|
|
|
|
/*
|
|
* 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 a parameter check
|
|
*/
|
|
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 1:
|
|
GET_USER_RET(*(u_char *)where, (u_char *) useraddr, -EFAULT);
|
|
break;
|
|
|
|
case 2:
|
|
GET_USER_RET(*(u_short *)where, (u_short *) useraddr, -EFAULT);
|
|
break;
|
|
|
|
case 4:
|
|
GET_USER_RET(*(u_long *)where, (u_long *) useraddr, -EFAULT);
|
|
break;
|
|
|
|
default:
|
|
copy_from_user(where, useraddr, size);
|
|
#else
|
|
case 1:
|
|
*(pUChar)where = *(pUChar)useraddr;
|
|
break;
|
|
|
|
case 2:
|
|
*(pUShort)where = *(pUShort)useraddr;
|
|
break;
|
|
|
|
case 4:
|
|
*(pULong)where = *(pULong)useraddr;
|
|
break;
|
|
|
|
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;
|
|
|
|
copy_to_user(useraddr, ptr, size );
|
|
#else
|
|
memcpy( useraddr, ptr, size );
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
|
|
#ifndef __KERNEL__
|
|
static void copy_from_user( pVoid dest, pVoid src, int len )
|
|
{
|
|
memcpy( dest, src, len );
|
|
}
|
|
|
|
static void copy_to_user( pVoid dest, pVoid src, int len )
|
|
{
|
|
memcpy( dest, src, len );
|
|
}
|
|
#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 1:
|
|
PUT_USER_RET((u_char)value, (u_char *) useraddr, -EFAULT);
|
|
break;
|
|
case 2:
|
|
PUT_USER_RET((u_short)value, (u_short *) useraddr, -EFAULT);
|
|
break;
|
|
case 4:
|
|
PUT_USER_RET((u_long)value, (u_long *) useraddr, -EFAULT);
|
|
break;
|
|
#else
|
|
case 1:
|
|
*(pUChar)useraddr = (UChar)value;
|
|
break;
|
|
case 2:
|
|
*(pUShort)useraddr = (UShort)value;
|
|
break;
|
|
case 4:
|
|
*(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( ULong 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%lu: 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 = (ULong)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%lx)\n", (ULong)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;
|
|
#endif
|
|
|
|
/*
|
|
* push some values from the struct
|
|
*/
|
|
#ifdef __KERNEL__
|
|
flags = ps->flags;
|
|
pd = ps->pardev;
|
|
pp = ps->pp;
|
|
procDir = ps->procDir;
|
|
#endif
|
|
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->pardev = pd;
|
|
ps->pp = pp;
|
|
ps->procDir = procDir;
|
|
#endif
|
|
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" );
|
|
copy_from_user(&version, arg, sizeof(UShort));
|
|
|
|
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" );
|
|
copy_from_user( &img, (pImgDef)arg, size);
|
|
|
|
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" );
|
|
|
|
copy_from_user(&adj, (pPPAdjDef)arg, sizeof(PPAdjDef));
|
|
|
|
DBG( DBG_LOW, "Adjusting device %lu\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" );
|
|
|
|
copy_from_user( &map, (pMapDef)arg, sizeof(MapDef));
|
|
|
|
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++ ) {
|
|
copy_from_user((pVoid)&ps->a_bMapTable[x_len * i],
|
|
map.map, x_len );
|
|
}
|
|
} else {
|
|
|
|
u_long idx = 0;
|
|
if( map.map_id == _MAP_GREEN )
|
|
idx = 1;
|
|
if( map.map_id == _MAP_BLUE )
|
|
idx = 2;
|
|
|
|
copy_from_user((pVoid)&ps->a_bMapTable[x_len * idx],
|
|
map.map, x_len );
|
|
}
|
|
|
|
/* 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" );
|
|
|
|
copy_from_user(&sInf, (pScanInfo)arg, sizeof(ScanInfo));
|
|
|
|
/*
|
|
* 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;
|
|
|
|
copy_to_user((pScanInfo)arg, &sInf, sizeof(ScanInfo));
|
|
}
|
|
}
|
|
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);
|
|
|
|
copy_to_user((pStartScan)arg, pstart, sizeof(StartScan));
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* stop scan */
|
|
case _PTDRV_STOP_SCAN:
|
|
|
|
DBG( DBG_LOW, "ioctl(_PTDRV_STOP_SCAN)\n" );
|
|
|
|
copy_from_user(&cancel, arg, sizeof(int));
|
|
|
|
/* 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" );
|
|
argVal = 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%lu: 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%lu: 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%lu: 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 > 1.0 ) {
|
|
|
|
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%lu: 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[%lu], scaleBuf = 0x%lx\n",
|
|
(ULong)ps->Scan.bp.pMonoBuf,
|
|
ps->DataInf.dwAppPhyBytesPerLine, (ULong)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%lu: 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 = %ld\n", ps->DataInf.dwAppLinesPerArea);
|
|
DBG( DBG_HIGH, "dwAppBytesPerLine = %ld\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 = %ld\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 %lu!\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 );
|
|
copy_to_user( buffer, scaleBuf,
|
|
ps->DataInf.dwAppPhyBytesPerLine);
|
|
} else {
|
|
copy_to_user( buffer, ps->Scan.bp.pMonoBuf,
|
|
ps->DataInf.dwAppPhyBytesPerLine);
|
|
}
|
|
|
|
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
|
|
*/
|
|
int init_module( void )
|
|
{
|
|
UInt devCount;
|
|
UInt i;
|
|
int retval = _OK;
|
|
int result = _OK;
|
|
#ifdef CONFIG_DEVFS_FS
|
|
char controlname[24];
|
|
#endif
|
|
|
|
DBG( DBG_HIGH, "*********************************************\n" );
|
|
DBG( DBG_HIGH, "pt_drv: init_module()\n" );
|
|
|
|
#ifdef CONFIG_DEVFS_FS
|
|
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 %d.%d-%d\n",
|
|
_PTDRV_V1, _PTDRV_V0, _PTDRV_BUILD );
|
|
|
|
/* register for 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
|
|
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 );
|
|
#endif
|
|
|
|
ProcFsRegisterDevice( PtDrvDevices[i] );
|
|
devCount++;
|
|
} else {
|
|
retval = result;
|
|
ptdrvShutdown( PtDrvDevices[i] );
|
|
PtDrvDevices[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if something went wrong, shutdown all...
|
|
*/
|
|
if( devCount == 0 ) {
|
|
|
|
#ifdef CONFIG_DEVFS_FS
|
|
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
|
|
*/
|
|
void cleanup_module( void )
|
|
{
|
|
UInt i;
|
|
pScanData ps;
|
|
#ifdef CONFIG_DEVFS_FS
|
|
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
|
|
sprintf( controlname, "scanner/pt_drv%d", i );
|
|
master = devfs_find_handle( NULL,controlname, 0, 0,
|
|
DEVFS_SPECIAL_CHR, 0 );
|
|
devfs_unregister( master );
|
|
#endif
|
|
ptdrvShutdown( ps );
|
|
ProcFsUnregisterDevice( ps );
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_DEVFS_FS
|
|
devfs_unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME );
|
|
#else
|
|
unregister_chrdev( _PTDRV_MAJOR, _DRV_NAME );
|
|
#endif
|
|
ProcFsShutdown();
|
|
|
|
DBG( DBG_HIGH, "pt_drv: cleanup done.\n" );
|
|
DBG( DBG_HIGH, "*********************************************\n" );
|
|
}
|
|
|
|
#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 %lu 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
|
|
*/
|
|
static int pt_drv_ioctl( struct inode *inode, struct file *file,
|
|
UInt cmd, ULong arg )
|
|
{
|
|
pScanData ps;
|
|
|
|
if ( !(ps = get_pt_from_inode(inode)) )
|
|
return(-ENXIO);
|
|
|
|
return ptdrvIoctl( ps, cmd, (pVoid)arg);
|
|
}
|
|
|
|
#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 ...................................................*/
|