Logo Search packages:      
Sourcecode: sane-backends version File versions

plustek-pp_ptdrv.c

/* @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-2004 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
 * .
 * <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 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

#if (defined(CONFIG_DEVFS_FS) && !defined(DEVFS_26_STYLE))
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)
{
    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 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 > 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%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
 */
#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

    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" );

      /* 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
#endif

                        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(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
#endif
                  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();

      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 %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 ...................................................*/

Generated by  Doxygen 1.6.0   Back to index