summaryrefslogtreecommitdiff
path: root/backend/plustek-usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/plustek-usb.c')
-rw-r--r--backend/plustek-usb.c1526
1 files changed, 1526 insertions, 0 deletions
diff --git a/backend/plustek-usb.c b/backend/plustek-usb.c
new file mode 100644
index 0000000..6c9e67a
--- /dev/null
+++ b/backend/plustek-usb.c
@@ -0,0 +1,1526 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usb.c
+ * @brief The interface functions to the USB driver stuff.
+ *
+ * Based on sources acquired from Plustek Inc.<br>
+ * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
+ *
+ * History:
+ * - 0.40 - starting version of the USB support
+ * - 0.41 - removed CHECK
+ * - added Canon to the manufacturer list
+ * - 0.42 - added warmup stuff
+ * - added setmap function
+ * - changed detection stuff, so we first check whether
+ * - the vendor and product Ids match with the ones in our list
+ * - 0.43 - cleanup
+ * - 0.44 - changes to integration CIS based devices
+ * - 0.45 - added skipFine assignment
+ * - added auto device name detection if only product and vendor id<br>
+ * has been specified
+ * - made 16-bit gray mode work
+ * - added special handling for Genius devices
+ * - added TPA autodetection for EPSON Photo
+ * - fixed bug that causes warmup each time autodetected<br>
+ * TPA on EPSON is used
+ * - removed Genius from PCB-Id check
+ * - added Compaq to the list
+ * - removed the scaler stuff for CIS devices
+ * - removed homeing stuff from readline function
+ * - fixed flag setting in usbDev_startScan()
+ * - 0.46 - added additional branch to support alternate calibration
+ * - 0.47 - added special handling with 0x400 vendor ID and model override
+ * - removed PATH_MAX
+ * - change usbDev_stopScan and usbDev_open prototype
+ * - cleanup
+ * - 0.48 - added function usb_CheckAndCopyAdjs()
+ * - 0.49 - changed autodetection
+ * - added support for LiDE25 (pid 0x2220)
+ * - 0.50 - minor fix for startup reset
+ * removed unnecessary calls to usbio_ResetLM983x()
+ * 1200DPI CIS devices don't use GrayFromColor any longer
+ * - 0.51 - added Syscan to the vendor list
+ * - added SCANFLAG_Calibration handling
+ * - 0.52 - added _WAF_LOFF_ON_START and _WAF_INC_DARKTGT
+ * handling in usbDev_startScan()
+ * added Visioneer
+ * .
+ * <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>
+ */
+
+/** useful for description tables
+ */
+typedef struct {
+ int id;
+ char *desc;
+ char *desc_alt;
+} TabDef, *pTabDef;
+
+/** to allow different vendors...
+ */
+static TabDef usbVendors[] = {
+
+ { 0x07B3, "Plustek", NULL },
+ { 0x0400, "NSC", "Mustek" },
+ { 0x0458, "KYE/Genius", NULL },
+ { 0x03F0, "Hewlett-Packard", NULL },
+ { 0x04B8, "Epson", NULL },
+ { 0x04A7, "Visioneer", NULL },
+ { 0x04A9, "Canon", NULL },
+ { 0x1606, "UMAX", NULL },
+ { 0x049F, "Compaq", NULL },
+ { 0x0A82, "Syscan", NULL },
+ { 0x0A53, "PandP Co., Ltd.", NULL },
+ { 0xFFFF, NULL, NULL }
+};
+
+/** we use at least 8 megs for scanning... */
+#define _SCANBUF_SIZE (8 * 1024 * 1024)
+
+/********************** the USB scanner interface ****************************/
+
+/** remove the slash out of the model-name to obtain a valid filename
+ */
+static SANE_Bool usb_normFileName( char *fname, char* buffer, u_long max_len )
+{
+ char *src, *dst;
+
+ if( NULL == fname )
+ return SANE_FALSE;
+
+ if( strlen( fname ) >= max_len )
+ return SANE_FALSE;
+
+ src = fname;
+ dst = buffer;
+ while( *src != '\0' ) {
+
+ if((*src == '/') || isspace(*src) || ispunct(*src))
+ *dst = '_';
+ else
+ *dst = *src;
+
+ dst++;
+ src++;
+ }
+ *dst = '\0';
+
+ return SANE_TRUE;
+}
+
+/** do some range checking and copy the adjustment values from the
+ * frontend to our internal structures, so that the backend can take
+ * care of them.
+ */
+static void usb_CheckAndCopyAdjs( Plustek_Device *dev )
+{
+ if( dev->adj.lampOff >= 0 )
+ dev->usbDev.dwLampOnPeriod = dev->adj.lampOff;
+
+ if( dev->adj.lampOffOnEnd >= 0 )
+ dev->usbDev.bLampOffOnEnd = dev->adj.lampOffOnEnd;
+
+ if( dev->adj.skipCalibration > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_BYPASS_CALIBRATION;
+
+ if( dev->adj.skipFine > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_SKIP_FINE;
+
+ if( dev->adj.skipFineWhite > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_SKIP_WHITEFINE;
+
+ if( dev->adj.incDarkTgt > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_INC_DARKTGT;
+
+ if( dev->adj.skipDarkStrip > 0 )
+ dev->usbDev.Caps.Normal.DarkShadOrgY = -1;
+
+ if( dev->adj.invertNegatives > 0 )
+ dev->usbDev.Caps.workaroundFlag |= _WAF_INV_NEGATIVE_MAP;
+}
+
+/**
+ * assign the values to the structures used by the currently found scanner
+ */
+static void
+usb_initDev( Plustek_Device *dev, int idx, int handle, int vendor )
+{
+ char *ptr;
+ char tmp_str1[PATH_MAX];
+ char tmp_str2[PATH_MAX];
+ int i;
+ ScanParam sParam;
+ u_short tmp = 0;
+
+ DBG( _DBG_INFO, "usb_initDev(%d,0x%04x,%i)\n",
+ idx, vendor, dev->initialized );
+ /* save capability flags... */
+ if( dev->initialized >= 0 ) {
+ tmp = DEVCAPSFLAG_TPA;
+ }
+
+ /* copy the original values... */
+ memcpy( &dev->usbDev.Caps, Settings[idx].pDevCaps, sizeof(DCapsDef));
+ memcpy( &dev->usbDev.HwSetting, Settings[idx].pHwDef, sizeof(HWDef));
+
+ /* restore capability flags... */
+ if( dev->initialized >= 0 ) {
+ dev->usbDev.Caps.wFlags |= tmp;
+ }
+
+ usb_CheckAndCopyAdjs( dev );
+ DBG( _DBG_INFO, "Device WAF : 0x%08lx\n", dev->usbDev.Caps.workaroundFlag );
+ DBG( _DBG_INFO, "Transferrate: %lu Bytes/s\n", dev->transferRate );
+
+ /* adjust data origin
+ */
+ dev->usbDev.Caps.Positive.DataOrigin.x -= dev->adj.tpa.x;
+ dev->usbDev.Caps.Positive.DataOrigin.y -= dev->adj.tpa.y;
+
+ dev->usbDev.Caps.Negative.DataOrigin.x -= dev->adj.neg.x;
+ dev->usbDev.Caps.Negative.DataOrigin.y -= dev->adj.neg.y;
+
+ dev->usbDev.Caps.Normal.DataOrigin.x -= dev->adj.pos.x;
+ dev->usbDev.Caps.Normal.DataOrigin.y -= dev->adj.pos.y;
+
+ /** adjust shading position
+ */
+ if( dev->adj.posShadingY >= 0 )
+ dev->usbDev.Caps.Normal.ShadingOriginY = dev->adj.posShadingY;
+
+ if( dev->adj.tpaShadingY >= 0 )
+ dev->usbDev.Caps.Positive.ShadingOriginY = dev->adj.tpaShadingY;
+
+ if( dev->adj.negShadingY >= 0 )
+ dev->usbDev.Caps.Negative.ShadingOriginY = dev->adj.negShadingY;
+
+ /* adjust the gamma settings... */
+ if( dev->adj.rgamma == 1.0 )
+ dev->adj.rgamma = dev->usbDev.HwSetting.gamma;
+ if( dev->adj.ggamma == 1.0 )
+ dev->adj.ggamma = dev->usbDev.HwSetting.gamma;
+ if( dev->adj.bgamma == 1.0 )
+ dev->adj.bgamma = dev->usbDev.HwSetting.gamma;
+ if( dev->adj.graygamma == 1.0 )
+ dev->adj.graygamma = dev->usbDev.HwSetting.gamma;
+
+ /* the following you normally get from the registry...
+ */
+ bMaxITA = 0; /* Maximum integration time adjust */
+
+ dev->usbDev.ModelStr = Settings[idx].pModelString;
+
+ dev->fd = handle;
+
+ if( dev->initialized < 0 ) {
+ if( usb_HasTPA( dev ))
+ dev->usbDev.Caps.wFlags |= DEVCAPSFLAG_TPA;
+ }
+ DBG( _DBG_INFO, "Device Flags: 0x%08x\n", dev->usbDev.Caps.wFlags );
+
+ /* well now we patch the vendor string...
+ * if not found, the default vendor will be Plustek
+ */
+ for( i = 0; usbVendors[i].desc != NULL; i++ ) {
+
+ if( usbVendors[i].id == vendor ) {
+ dev->sane.vendor = usbVendors[i].desc;
+ if (dev->usbDev.Caps.workaroundFlag & _WAF_USE_ALT_DESC )
+ if (usbVendors[i].desc_alt )
+ dev->sane.vendor = usbVendors[i].desc_alt;
+ DBG( _DBG_INFO, "Vendor adjusted to: >%s<\n", dev->sane.vendor );
+ break;
+ }
+ }
+
+ dev->usbDev.dwTicksLampOn = 0;
+ dev->usbDev.currentLamp = usb_GetLampStatus( dev );
+ usb_ResetRegisters( dev );
+
+ if( dev->initialized >= 0 )
+ return;
+
+ usb_IsScannerReady( dev );
+
+ sParam.bBitDepth = 8;
+ sParam.bCalibration = PARAM_Scan;
+ sParam.bChannels = 3;
+ sParam.bDataType = SCANDATATYPE_Color;
+ sParam.bSource = SOURCE_Reflection;
+ sParam.Origin.x = 0;
+ sParam.Origin.y = 0;
+ sParam.UserDpi.x = 150;
+ sParam.UserDpi.y = 150;
+ sParam.dMCLK = 4;
+ sParam.Size.dwPixels = 0;
+
+ /* create calibration-filename */
+ sprintf( tmp_str2, "%s-%s",
+ dev->sane.vendor, dev->usbDev.ModelStr );
+
+ if( !usb_normFileName( tmp_str2, tmp_str1, PATH_MAX )) {
+ strcpy( tmp_str1, "plustek-default" );
+ }
+
+ ptr = getenv ("HOME");
+ if( NULL == ptr ) {
+ sprintf( tmp_str2, "/tmp/%s", tmp_str1 );
+ } else {
+ sprintf( tmp_str2, "%s/.sane/%s", ptr, tmp_str1 );
+ }
+ dev->calFile = strdup( tmp_str2 );
+ DBG( _DBG_INFO, "Calibration file-names set to:\n" );
+ DBG( _DBG_INFO, ">%s-coarse.cal<\n", dev->calFile );
+ DBG( _DBG_INFO, ">%s-fine.cal<\n", dev->calFile );
+
+ /* initialize the ASIC registers */
+ usb_SetScanParameters( dev, &sParam );
+
+ /* check and move sensor to its home position */
+ usb_ModuleToHome( dev, SANE_FALSE );
+
+ /* set the global flag, that we are initialized so far */
+ dev->initialized = idx;
+}
+
+/**
+ * will be used for retrieving a Plustek device
+ */
+static int usb_CheckForPlustekDevice( int handle, Plustek_Device *dev )
+{
+ char tmp[50];
+ char pcbStr[10];
+ u_char reg59[3], reg59s[3], pcbID;
+ int i, result;
+
+ /*
+ * Plustek uses the misc IO 12 to get the PCB ID
+ * (PCB = printed circuit board), so it's possible to have one
+ * product ID and up to 7 different devices...
+ */
+ DBG( _DBG_INFO, "Trying to get the pcbID of a Plustek device...\n" );
+
+ /* get the PCB-ID */
+ result = sanei_lm983x_read( handle, 0x59, reg59s, 3, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ reg59[0] = 0x22; /* PIO1: Input, PIO2: Input */
+ reg59[1] = 0x02; /* PIO3: Input, PIO4: Output as low */
+ reg59[2] = 0x03;
+
+ result = sanei_lm983x_write( handle, 0x59, reg59, 3, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ result = sanei_lm983x_read ( handle, 0x02, &pcbID, 1, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ pcbID = (u_char)((pcbID >> 2) & 0x07);
+
+ result = sanei_lm983x_read( handle, 0x59, reg59s, 3, SANE_TRUE );
+ if( SANE_STATUS_GOOD != result ) {
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ DBG( _DBG_INFO, "pcbID=0x%02x\n", pcbID );
+
+ /* now roam through the setting list... */
+ strncpy( tmp, dev->usbId, 13 );
+ tmp[13] = '\0';
+
+ sprintf( pcbStr, "-%u", pcbID );
+ strcat ( tmp, pcbStr );
+
+ DBG( _DBG_INFO, "Checking for device >%s<\n", tmp );
+
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ if( 0 == strcmp( Settings[i].pIDString, tmp )) {
+ DBG(_DBG_INFO, "Device description for >%s< found.\n", tmp );
+ usb_initDev( dev, i, handle, dev->usbDev.vendor );
+ return handle;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * will be called upon sane_exit
+ */
+static void usbDev_shutdown( Plustek_Device *dev )
+{
+ SANE_Int handle;
+
+ DBG( _DBG_INFO, "Shutdown called (dev->fd=%d, %s)\n",
+ dev->fd, dev->sane.name );
+ if( NULL == dev->usbDev.ModelStr ) {
+ DBG( _DBG_INFO, "Function ignored!\n" );
+ return;
+ }
+
+ if( SANE_STATUS_GOOD == sanei_usb_open( dev->sane.name, &handle )) {
+
+ dev->fd = handle;
+
+ DBG( _DBG_INFO, "Waiting for scanner-ready...\n" );
+ usb_IsScannerReady( dev );
+
+ if( 0 != dev->usbDev.bLampOffOnEnd ) {
+
+ DBG( _DBG_INFO, "Switching lamp off...\n" );
+ usb_LampOn( dev, SANE_FALSE, SANE_FALSE );
+ }
+ dev->fd = -1;
+ sanei_usb_close( handle );
+ }
+ usb_StopLampTimer( dev );
+}
+
+/**
+ * This function checks wether a device, described by a given
+ * string(vendor and product ID), is support by this backend or not
+ *
+ * @param usbIdStr - sting consisting out of product and vendor ID
+ * format: "0xVVVVx0xPPPP" VVVV = Vendor ID, PPP = Product ID
+ * @returns; SANE_TRUE if supported, SANE_FALSE if not
+ */
+static SANE_Bool usb_IsDeviceInList( char *usbIdStr )
+{
+ int i;
+
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ if( 0 == strncmp( Settings[i].pIDString, usbIdStr, 13 ))
+ return SANE_TRUE;
+ }
+ return SANE_FALSE;
+}
+
+/** get last valid entry of a list
+ */
+static DevList*
+getLast( DevList *l )
+{
+ if( l == NULL )
+ return NULL;
+
+ while( l->next != NULL )
+ l = l->next;
+ return l;
+}
+
+/** add a new entry to our internal device list, when a device is detected
+ */
+static SANE_Status usb_attach( SANE_String_Const dev_name )
+{
+ int len;
+ DevList *tmp, *last;
+
+ /* get some memory for the entry... */
+ len = sizeof(DevList) + strlen(dev_name) + 1;
+ tmp = (DevList*)malloc(len);
+
+ /* initialize and set the values */
+ memset(tmp, 0, len );
+
+ /* the device name is part of our structure space */
+ tmp->dev_name = &((char*)tmp)[sizeof(DevList)];
+ strcpy( tmp->dev_name, dev_name );
+ tmp->attached = SANE_FALSE;
+
+ /* root set ? */
+ if( usbDevs == NULL ) {
+ usbDevs = tmp;
+ } else {
+ last = getLast(usbDevs); /* append... */
+ last->next = tmp;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ */
+static void
+usbGetList( DevList **devs )
+{
+ int i;
+ SANE_Bool il;
+ SANE_Word v, p;
+ DevList *tmp;
+
+ DBG( _DBG_INFO, "Retrieving all supported and conntected devices\n" );
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ v = strtol( &(Settings[i].pIDString)[0], 0, 0 );
+ p = strtol( &(Settings[i].pIDString)[7], 0, 0 );
+
+ /* check if this vendor- and product-ID has already been added, needed
+ * for Plustek devices - here one product-ID is used for more than one
+ * device type...
+ */
+ il = SANE_FALSE;
+ for( tmp = *devs; tmp ; tmp = tmp->next ) {
+ if( tmp->device_id == p && tmp->vendor_id == v ) {
+ il = SANE_TRUE;
+ break;
+ }
+ }
+ if( il ) {
+ DBG( _DBG_INFO2, "Already in list: 0x%04x-0x%04x\n", v, p );
+ continue;
+ }
+
+ /* get the last entry... */
+ tmp = getLast(*devs);
+ DBG( _DBG_INFO2, "Checking for 0x%04x-0x%04x\n", v, p );
+ sanei_usb_find_devices( v, p, usb_attach );
+
+ if( getLast(*devs) != tmp ) {
+
+ if( tmp == NULL )
+ tmp = *devs;
+ else
+ tmp = tmp->next;
+
+ while( tmp != NULL ) {
+ tmp->vendor_id = v;
+ tmp->device_id = p;
+ tmp = tmp->next;
+ }
+ }
+ }
+
+ DBG( _DBG_INFO, "Available and supported devices:\n" );
+ if( *devs == NULL )
+ DBG( _DBG_INFO, "NONE.\n" );
+
+ for( tmp = *devs; tmp; tmp = tmp->next ) {
+ DBG( _DBG_INFO, "Device: >%s< - 0x%04xx0x%04x\n",
+ tmp->dev_name, tmp->vendor_id, tmp->device_id );
+ }
+}
+
+/**
+ */
+static int usbDev_open( Plustek_Device *dev, DevList *devs, int keep_lock )
+{
+ char dn[512];
+ char devStr[50];
+ int result;
+ int i;
+ int lc;
+ SANE_Int handle;
+ SANE_Byte version;
+ SANE_Word vendor, product;
+ SANE_Bool was_empty;
+ SANE_Status status;
+ DevList *tmp;
+
+ DBG( _DBG_INFO, "usbDev_open(%s,%s) - %p\n",
+ dev->name, dev->usbId, (void*)devs );
+
+ /* preset our internal usb device structure */
+ memset( &dev->usbDev, 0, sizeof(DeviceDef));
+
+ /* devs is NULL, when called from sane_start */
+ if( devs ) {
+
+ dn[0] = '\0';
+ if( !strcmp( dev->name, "auto" )) {
+
+ /* use the first "unattached"... */
+ for( tmp = devs; tmp; tmp = tmp->next ) {
+ if( !tmp->attached ) {
+ tmp->attached = SANE_TRUE;
+ strcpy( dn, tmp->dev_name );
+ break;
+ }
+ }
+ } else {
+
+ vendor = strtol( &dev->usbId[0], 0, 0 );
+ product = strtol( &dev->usbId[7], 0, 0 );
+
+ /* check the first match... */
+ for( tmp = devs; tmp; tmp = tmp->next ) {
+ if( tmp->vendor_id == vendor && tmp->device_id == product ) {
+ if( !tmp->attached ) {
+ tmp->attached = SANE_TRUE;
+ strcpy( dn, tmp->dev_name );
+ break;
+ }
+ }
+ }
+ }
+
+ if( !dn[0] ) {
+ DBG( _DBG_ERROR, "No supported device found!\n" );
+ return -1;
+ }
+
+ status = sanei_access_lock( dn, 5 );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_access_lock failed: %d\n", status );
+ return -1;
+ }
+
+ status = sanei_usb_open( dn, &handle );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_usb_open failed: %s (%d)\n",
+ strerror(errno), errno);
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+
+ /* replace the old devname, so we are able to have multiple
+ * auto-detected devices
+ */
+ free( dev->name );
+ dev->name = strdup(dn);
+ dev->sane.name = dev->name;
+
+ } else {
+
+ status = sanei_access_lock( dev->sane.name, 5 );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_access_lock failed: %d\n", status );
+ return -1;
+ }
+
+ status = sanei_usb_open( dev->name, &handle );
+ if( SANE_STATUS_GOOD != status ) {
+ DBG( _DBG_ERROR, "sanei_usb_open failed: %s (%d)\n",
+ strerror(errno), errno);
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+ }
+
+ was_empty = SANE_FALSE;
+
+ result = sanei_usb_get_vendor_product( handle, &vendor, &product );
+
+ if( SANE_STATUS_GOOD == result ) {
+
+ sprintf( devStr, "0x%04X-0x%04X", vendor, product );
+
+ DBG(_DBG_INFO,"Vendor ID=0x%04X, Product ID=0x%04X\n",vendor,product);
+
+ if( dev->usbId[0] != '\0' ) {
+
+ if( 0 != strcmp( dev->usbId, devStr )) {
+ DBG( _DBG_ERROR, "Specified Vendor and Product ID "
+ "doesn't match with the ones\n"
+ "in the config file\n" );
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ return -1;
+ }
+ } else {
+ sprintf( dev->usbId, "0x%04X-0x%04X", vendor, product );
+ was_empty = SANE_TRUE;
+ }
+
+ } else {
+
+ DBG( _DBG_INFO, "Can't get vendor & product ID from driver...\n" );
+
+ /* if the ioctl stuff is not supported by the kernel and we have
+ * nothing specified, we have to give up...
+ */
+ if( dev->usbId[0] == '\0' ) {
+ DBG( _DBG_ERROR, "Cannot autodetect Vendor an Product ID, "
+ "please specify in config file.\n" );
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ vendor = strtol( &dev->usbId[0], 0, 0 );
+ product = strtol( &dev->usbId[7], 0, 0 );
+ DBG( _DBG_INFO, "... using the specified: "
+ "0x%04X-0x%04X\n", vendor, product );
+ }
+
+ /* before accessing the scanner, check if supported!
+ */
+ if( !usb_IsDeviceInList( dev->usbId )) {
+ DBG( _DBG_ERROR, "Device >%s<, is not supported!\n", dev->usbId );
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ return -1;
+ }
+
+ status = usbio_DetectLM983x( handle, &version );
+ if( SANE_STATUS_GOOD != status ) {
+ sanei_usb_close( handle );
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+
+ if ((version < 3) || (version > 4)) {
+ DBG( _DBG_ERROR, "This is not a LM9831 or LM9832 chip based scanner.\n" );
+ sanei_usb_close( handle );
+ sanei_access_unlock( dev->sane.name );
+ return -1;
+ }
+
+ /* need to set the handle and the detected chiptype... */
+ dev->fd = handle;
+ dev->usbDev.HwSetting.chip = (version==3 ? _LM9831:_LM9832);
+ usbio_ResetLM983x( dev );
+ dev->fd = -1;
+
+ dev->usbDev.vendor = vendor;
+ dev->usbDev.product = product;
+
+ DBG( _DBG_INFO, "Detected vendor & product ID: "
+ "0x%04X-0x%04X\n", vendor, product );
+
+ /*
+ * Plustek uses the misc IO 1/2 to get the PCB ID
+ * (PCB = printed circuit board), so it's possible to have one
+ * product ID and up to 7 different devices...
+ */
+ if( 0x07B3 == vendor ) {
+
+ handle = usb_CheckForPlustekDevice( handle, dev );
+
+ if( was_empty )
+ dev->usbId[0] = '\0';
+
+ if( handle >= 0 ) {
+ if( !keep_lock )
+ sanei_access_unlock( dev->sane.name );
+ return handle;
+ }
+
+ } else {
+
+ /* now roam through the setting list... */
+ lc = 13;
+ strncpy( devStr, dev->usbId, lc );
+ devStr[lc] = '\0';
+
+ if( 0x400 == vendor ) {
+ if((dev->adj.mov < 0) || (dev->adj.mov > 1)) {
+ DBG(_DBG_INFO, "BearPaw MOV out of range: %d\n", dev->adj.mov);
+ dev->adj.mov = 0;
+ }
+ sprintf( devStr, "%s-%d", dev->usbId, dev->adj.mov );
+ lc = strlen(devStr);
+ DBG( _DBG_INFO, "BearPaw device: %s (%d)\n", devStr, lc );
+ }
+
+ if( was_empty )
+ dev->usbId[0] = '\0';
+
+ /* if we don't use the PCD ID extension...
+ */
+ for( i = 0; NULL != Settings[i].pIDString; i++ ) {
+
+ if( 0 == strncmp( Settings[i].pIDString, devStr, lc )) {
+ DBG( _DBG_INFO, "Device description for >%s< found.\n", devStr );
+ usb_initDev( dev, i, handle, vendor );
+ if( !keep_lock )
+ sanei_access_unlock( dev->sane.name );
+ return handle;
+ }
+ }
+ }
+
+ sanei_access_unlock( dev->sane.name );
+ sanei_usb_close( handle );
+ DBG( _DBG_ERROR, "No matching device found >%s<\n", devStr );
+ return -1;
+}
+
+/**
+ */
+static int usbDev_close( Plustek_Device *dev )
+{
+ DBG( _DBG_INFO, "usbDev_close()\n" );
+ sanei_usb_close( dev->fd );
+ dev->fd = -1;
+ return 0;
+}
+
+/** convert the stuff
+ */
+static int usbDev_getCaps( Plustek_Device *dev )
+{
+ DCapsDef *scaps = &dev->usbDev.Caps;
+
+ DBG( _DBG_INFO, "usbDev_getCaps()\n" );
+
+ dev->caps.dwFlag = 0;
+
+ if(((DEVCAPSFLAG_Positive & scaps->wFlags) &&
+ (DEVCAPSFLAG_Negative & scaps->wFlags)) ||
+ (DEVCAPSFLAG_TPA & scaps->wFlags)) {
+ dev->caps.dwFlag |= SFLAG_TPA;
+ }
+
+ dev->caps.wMaxExtentX = scaps->Normal.Size.x;
+ dev->caps.wMaxExtentY = scaps->Normal.Size.y;
+ return 0;
+}
+
+/** usbDev_getCropInfo
+ * function to set the image relevant stuff
+ */
+static int usbDev_getCropInfo( Plustek_Device *dev, CropInfo *ci )
+{
+ WinInfo size;
+
+ DBG( _DBG_INFO, "usbDev_getCropInfo()\n" );
+
+ usb_GetImageInfo( dev, &ci->ImgDef, &size );
+
+ ci->dwPixelsPerLine = size.dwPixels;
+ ci->dwLinesPerArea = size.dwLines;
+ ci->dwBytesPerLine = size.dwBytes;
+
+ if( ci->ImgDef.dwFlag & SCANFLAG_DWORDBoundary )
+ ci->dwBytesPerLine = (ci->dwBytesPerLine + 3UL) & 0xfffffffcUL;
+
+ DBG( _DBG_INFO, "PPL = %lu\n", ci->dwPixelsPerLine );
+ DBG( _DBG_INFO, "LPA = %lu\n", ci->dwLinesPerArea );
+ DBG( _DBG_INFO, "BPL = %lu\n", ci->dwBytesPerLine );
+
+ return 0;
+}
+
+/**
+ */
+static int usbDev_setMap( Plustek_Device *dev, SANE_Word *map,
+ SANE_Word length, SANE_Word channel )
+{
+ SANE_Word i, idx;
+
+ DBG(_DBG_INFO,"Setting map[%u] at 0x%08lx\n",channel,(unsigned long)map);
+
+ _VAR_NOT_USED( dev );
+
+ if( channel == _MAP_MASTER ) {
+
+ for( i = 0; i < length; i++ ) {
+ a_bMap[i] = (SANE_Byte)map[i];
+ a_bMap[length +i] = (SANE_Byte)map[i];
+ a_bMap[(length*2)+i] = (SANE_Byte)map[i];
+ }
+
+ } else {
+
+ idx = 0;
+ if( channel == _MAP_GREEN )
+ idx = 1;
+ if( channel == _MAP_BLUE )
+ idx = 2;
+
+ for( i = 0; i < length; i++ ) {
+ a_bMap[(length * idx)+i] = (SANE_Byte)map[i];
+ }
+ }
+
+ return 0;
+}
+
+/**
+ */
+static int
+usbDev_setScanEnv( Plustek_Device *dev, ScanInfo *si )
+{
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *caps = &dev->usbDev.Caps;
+
+ DBG( _DBG_INFO, "usbDev_setScanEnv()\n" );
+
+ /* clear all the stuff */
+ memset( scan, 0, sizeof(ScanDef));
+
+ if((si->ImgDef.dwFlag & SCANDEF_Adf) &&
+ (si->ImgDef.dwFlag & SCANDEF_ContinuousScan)) {
+ scan->sParam.dMCLK = dMCLK_ADF;
+ }
+
+ /* Save necessary informations */
+ scan->fGrayFromColor = 0;
+
+ /* for some devices and settings, we tweak the physical settings
+ * how to get the image - but not in calibration mode
+ */
+ if((si->ImgDef.dwFlag & SCANFLAG_Calibration) == 0) {
+
+ if( si->ImgDef.wDataType == COLOR_256GRAY ) {
+
+ if( !(si->ImgDef.dwFlag & SCANDEF_Adf) && !usb_IsCISDevice(dev) &&
+ (caps->OpticDpi.x == 1200 && si->ImgDef.xyDpi.x <= 300)) {
+ scan->fGrayFromColor = 2;
+ si->ImgDef.wDataType = COLOR_TRUE24;
+ DBG( _DBG_INFO, "* Gray from color set!\n" );
+ }
+
+ if( caps->workaroundFlag & _WAF_GRAY_FROM_COLOR ) {
+ DBG( _DBG_INFO, "* Gray(8-bit) from color set!\n" );
+ scan->fGrayFromColor = 2;
+ si->ImgDef.wDataType = COLOR_TRUE24;
+ }
+
+ } else if ( si->ImgDef.wDataType == COLOR_GRAY16 ) {
+ if( caps->workaroundFlag & _WAF_GRAY_FROM_COLOR ) {
+ DBG( _DBG_INFO, "* Gray(16-bit) from color set!\n" );
+ scan->fGrayFromColor = 2;
+ si->ImgDef.wDataType = COLOR_TRUE48;
+ }
+
+ } else if ( si->ImgDef.wDataType == COLOR_BW ) {
+ if( caps->workaroundFlag & _WAF_BIN_FROM_COLOR ) {
+ DBG( _DBG_INFO, "* Binary from color set!\n" );
+ scan->fGrayFromColor = 10;
+ si->ImgDef.wDataType = COLOR_TRUE24;
+ }
+ }
+ }
+
+ usb_SaveImageInfo( dev, &si->ImgDef );
+ usb_GetImageInfo ( dev, &si->ImgDef, &scan->sParam.Size );
+
+ /* mask the flags */
+ scan->dwFlag = si->ImgDef.dwFlag &
+ (SCANFLAG_bgr | SCANFLAG_BottomUp | SCANFLAG_Calibration |
+ SCANFLAG_DWORDBoundary | SCANFLAG_RightAlign |
+ SCANFLAG_StillModule | SCANDEF_Adf | SCANDEF_ContinuousScan);
+
+ if( !(SCANDEF_QualityScan & si->ImgDef.dwFlag)) {
+ DBG( _DBG_INFO, "* Preview Mode set!\n" );
+ } else {
+ DBG( _DBG_INFO, "* Preview Mode NOT set!\n" );
+ scan->dwFlag |= SCANDEF_QualityScan;
+ }
+
+ scan->sParam.brightness = si->siBrightness;
+ scan->sParam.contrast = si->siContrast;
+
+ if( scan->sParam.bBitDepth <= 8 )
+ scan->dwFlag &= ~SCANFLAG_RightAlign;
+
+ if( scan->dwFlag & SCANFLAG_DWORDBoundary ) {
+ if( scan->fGrayFromColor && scan->fGrayFromColor < 10)
+ scan->dwBytesLine = (scan->sParam.Size.dwBytes / 3 + 3) & 0xfffffffcUL;
+ else
+ scan->dwBytesLine = (scan->sParam.Size.dwBytes + 3UL) & 0xfffffffcUL;
+
+ } else {
+
+ if( scan->fGrayFromColor && scan->fGrayFromColor < 10)
+ scan->dwBytesLine = scan->sParam.Size.dwBytes / 3;
+ else
+ scan->dwBytesLine = scan->sParam.Size.dwBytes;
+ }
+
+ /* on CIS based devices we have to reconfigure the illumination
+ * settings for the gray modes
+ */
+ usb_AdjustCISLampSettings( dev, SANE_TRUE );
+
+ if( scan->dwFlag & SCANFLAG_BottomUp)
+ scan->lBufAdjust = -(long)scan->dwBytesLine;
+ else
+ scan->lBufAdjust = scan->dwBytesLine;
+
+ /* LM9831 has a BUG in 16-bit mode,
+ * so we generate pseudo 16-bit data from 8-bit
+ */
+ if( scan->sParam.bBitDepth > 8 ) {
+
+ if( _LM9831 == dev->usbDev.HwSetting.chip ) {
+
+ scan->sParam.bBitDepth = 8;
+ scan->dwFlag |= SCANFLAG_Pseudo48;
+ scan->sParam.Size.dwBytes >>= 1;
+ }
+ }
+
+ /* Source selection */
+ if( scan->sParam.bSource == SOURCE_Reflection ) {
+
+ dev->usbDev.pSource = &caps->Normal;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Normal.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Normal.lUp;
+
+ } else if( scan->sParam.bSource == SOURCE_Transparency ) {
+
+ dev->usbDev.pSource = &caps->Positive;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Positive.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Positive.lUp;
+
+ } else if( scan->sParam.bSource == SOURCE_Negative ) {
+
+ dev->usbDev.pSource = &caps->Negative;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Negative.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Negative.lUp;
+
+ } else {
+
+ dev->usbDev.pSource = &dev->usbDev.Caps.Adf;
+ scan->sParam.Origin.x += dev->usbDev.pSource->DataOrigin.x +
+ (u_long)dev->usbDev.Normal.lLeft;
+ scan->sParam.Origin.y += dev->usbDev.pSource->DataOrigin.y +
+ (u_long)dev->usbDev.Normal.lUp;
+ }
+
+ if( scan->sParam.bSource == SOURCE_ADF ) {
+
+ if( scan->dwFlag & SCANDEF_ContinuousScan )
+ dev->usbDev.fLastScanIsAdf = SANE_TRUE;
+ else
+ dev->usbDev.fLastScanIsAdf = SANE_FALSE;
+ }
+
+ return 0;
+}
+
+/**
+ */
+static int
+usbDev_stopScan( Plustek_Device *dev )
+{
+ DBG( _DBG_INFO, "usbDev_stopScan()\n" );
+
+ /* in cancel-mode we first stop the motor */
+ usb_ScanEnd( dev );
+
+ dev->scanning.dwFlag = 0;
+
+ if( NULL != dev->scanning.pScanBuffer ) {
+
+ free( dev->scanning.pScanBuffer );
+ dev->scanning.pScanBuffer = NULL;
+ usb_StartLampTimer( dev );
+ }
+ return 0;
+}
+
+/**
+ */
+static int
+usbDev_startScan( Plustek_Device *dev )
+{
+ ScanDef *scan = &dev->scanning;
+ DBG( _DBG_INFO, "usbDev_startScan()\n" );
+
+/* HEINER: Preview currently not working correctly */
+#if 0
+ if( scan->dwFlag & SCANDEF_QualityScan )
+ dev->usbDev.a_bRegs[0x0a] = 0;
+ else
+ dev->usbDev.a_bRegs[0x0a] = 1;
+#endif
+ dev->usbDev.a_bRegs[0x0a] = 0;
+
+ if((scan->dwFlag & SCANDEF_Adf) && (scan->dwFlag & SCANDEF_ContinuousScan)) {
+ scan->fCalibrated = SANE_TRUE;
+ } else {
+ scan->fCalibrated = SANE_FALSE;
+ }
+
+ scan->sParam.PhyDpi.x = usb_SetAsicDpiX(dev,scan->sParam.UserDpi.x);
+ scan->sParam.PhyDpi.y = usb_SetAsicDpiY(dev,scan->sParam.UserDpi.y);
+
+ /* Allocate shading/scan buffer */
+ scan->pScanBuffer = (u_long*)malloc( _SCANBUF_SIZE );
+
+ if( scan->pScanBuffer == NULL )
+ return _E_ALLOC;
+
+ scan->dwFlag |= SCANFLAG_StartScan;
+
+ /* some devices (esp. BearPaw) do need a lamp switch off before
+ * switching it on again. Otherwise it might happen that the lamp
+ * remains off
+ */
+ if(dev->usbDev.Caps.workaroundFlag & _WAF_LOFF_ON_START) {
+ if (usb_GetLampStatus(dev))
+ usb_LampOn( dev, SANE_FALSE, SANE_TRUE );
+ }
+
+ usb_LampOn( dev, SANE_TRUE, SANE_TRUE );
+
+ m_fStart = m_fFirst = SANE_TRUE;
+ m_fAutoPark = (scan->dwFlag&SCANFLAG_StillModule)?SANE_FALSE:SANE_TRUE;
+
+ if( usb_IsSheetFedDevice(dev))
+ if(usb_InCalibrationMode(dev))
+ m_fAutoPark = SANE_FALSE;
+
+ usb_StopLampTimer( dev );
+ return 0;
+}
+
+/**
+ * do the reading stuff here...
+ * first we perform the calibration step, and then we read the image
+ * line for line
+ */
+static int
+usbDev_Prepare( Plustek_Device *dev, SANE_Byte *buf )
+{
+ int result;
+ SANE_Bool use_alt_cal = SANE_FALSE;
+ ScanDef *scan = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ DBG( _DBG_INFO, "usbDev_PrepareScan()\n" );
+
+ /* check the current position of the sensor and move it back
+ * to it's home position if necessary...
+ */
+ if( !usb_IsSheetFedDevice(dev))
+ usb_SensorStatus( dev );
+
+ /* CIS devices need special handling... */
+ if( usb_IsCISDevice(dev)) {
+ use_alt_cal = SANE_TRUE;
+
+ } else {
+
+ if( dev->adj.altCalibrate )
+ use_alt_cal = SANE_TRUE;
+ }
+
+ /* for the skip functionality use the "old" calibration functions */
+ if( dev->usbDev.Caps.workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
+ use_alt_cal = SANE_FALSE;
+ }
+
+ if( use_alt_cal ) {
+ result = cano_DoCalibration( dev );
+ } else {
+ result = usb_DoCalibration( dev );
+ }
+
+ if( SANE_TRUE != result ) {
+ DBG( _DBG_ERROR, "calibration failed!!!\n" );
+ return _E_ABORT;
+ }
+
+ if( dev->adj.cacheCalData )
+ usb_SaveCalData( dev );
+
+ DBG( _DBG_INFO, "calibration done.\n" );
+ if( usb_InCalibrationMode(dev))
+ return 0;
+
+ if( !(scan->dwFlag & SCANFLAG_Scanning)) {
+
+ usleep( 10 * 1000 );
+
+ /* need to preset that here, as we need it during parameter setting
+ */
+ scan->bLinesToSkip = (u_char)(scan->sParam.PhyDpi.y / 50);
+
+ scan->dwLinesDiscard = 0;
+ if( scan->sParam.bChannels == 3 ) {
+ scan->dwLinesDiscard = (u_long)scaps->bSensorDistance *
+ scan->sParam.PhyDpi.y / scaps->OpticDpi.y;
+ scan->dwLinesDiscard <<= 1;
+ }
+
+ if( !usb_SetScanParameters( dev, &scan->sParam )) {
+ DBG( _DBG_ERROR, "Setting Scan Parameters failed!\n" );
+ return 0;
+ }
+
+ /* if we bypass the calibration step, we wait on lamp warmup here...
+ */
+ if( scaps->workaroundFlag & _WAF_BYPASS_CALIBRATION ) {
+ if( !usb_Wait4Warmup( dev )) {
+ DBG( _DBG_INFO, "usbDev_Prepare() - Cancel detected...\n" );
+ return 0;
+ }
+ }
+
+ scan->pbScanBufBegin = (u_char*)scan->pScanBuffer;
+
+ if((dev->caps.dwFlag & SFLAG_ADF) && (scaps->OpticDpi.x == 600))
+ scan->dwLinesScanBuf = 8;
+ else
+ scan->dwLinesScanBuf = 32;
+
+ scan->dwBytesScanBuf = scan->dwLinesScanBuf *
+ scan->sParam.Size.dwPhyBytes;
+
+ scan->dwNumberOfScanBufs = _SCANBUF_SIZE / scan->dwBytesScanBuf;
+ scan->dwLinesPerScanBufs = scan->dwNumberOfScanBufs *
+ scan->dwLinesScanBuf;
+ scan->pbScanBufEnd = scan->pbScanBufBegin +
+ scan->dwLinesPerScanBufs *
+ scan->sParam.Size.dwPhyBytes;
+ scan->dwRedShift = 0;
+ scan->dwBlueShift = 0;
+ scan->dwGreenShift = 0;
+
+ /* CCD scanner */
+ if( scan->sParam.bChannels == 3 ) {
+
+ scan->dwLinesDiscard = (u_long)scaps->bSensorDistance *
+ scan->sParam.PhyDpi.y / scaps->OpticDpi.y;
+
+ switch( scaps->bSensorOrder ) {
+
+ case SENSORORDER_rgb:
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_rbg:
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_gbr:
+ scan->Green.pb = scan->pbScanBufBegin;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_grb:
+ scan->Green.pb = scan->pbScanBufBegin;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_brg:
+ scan->Blue.pb = scan->pbScanBufBegin;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ break;
+
+ case SENSORORDER_bgr:
+ scan->Blue.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes;
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwLinesDiscard *
+ scan->sParam.Size.dwPhyBytes * 2UL;
+ }
+
+ /* double it for last channel */
+ scan->dwLinesDiscard <<= 1;
+ scan->dwGreenShift = (7UL+scan->sParam.bBitDepth) >> 3;
+ scan->Green.pb += scan->dwGreenShift;
+ scan->Blue.pb += (scan->dwGreenShift * 2);
+
+ if( scan->dwFlag & SCANFLAG_bgr) {
+
+ u_char *pb = scan->Blue.pb;
+
+ scan->Blue.pb = scan->Red.pb;
+ scan->Red.pb = pb;
+ scan->dwBlueShift = 0;
+ scan->dwRedShift = scan->dwGreenShift << 1;
+ } else {
+ scan->dwRedShift = 0;
+ scan->dwBlueShift = scan->dwGreenShift << 1;
+ }
+
+ } else {
+
+ /* CIS section */
+
+ /* this might be a simple gray operation or AFE 1 channel op */
+ scan->dwLinesDiscard = 0;
+ scan->Green.pb = scan->pbScanBufBegin;
+
+ if(( scan->sParam.bDataType == SCANDATATYPE_Color ) &&
+ ( hw->bReg_0x26 & _ONE_CH_COLOR )) {
+
+ u_char so;
+ u_long len = scan->sParam.Size.dwPhyBytes / 3;
+
+ so = scaps->bSensorOrder;
+ if (_WAF_RESET_SO_TO_RGB & scaps->workaroundFlag) {
+ if (scaps->bPCB != 0) {
+ if (scan->sParam.PhyDpi.x > scaps->bPCB) {
+ so = SENSORORDER_rgb;
+ DBG(_DBG_INFO, "* Resetting sensororder to RGB\n");
+ }
+ }
+ }
+
+ switch( so ) {
+
+ case SENSORORDER_rgb:
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + len;
+ scan->Blue.pb = scan->pbScanBufBegin + len * 2UL;
+ break;
+
+ case SENSORORDER_gbr:
+ scan->Green.pb = scan->pbScanBufBegin;
+ scan->Blue.pb = scan->pbScanBufBegin + len;
+ scan->Red.pb = scan->pbScanBufBegin + len * 2UL;
+ break;
+ default:
+ DBG( _DBG_ERROR, "CIS: This bSensorOrder "
+ "is not defined\n" );
+ return _E_INTERNAL;
+ }
+ }
+ }
+
+ /* set a funtion to process the RAW data... */
+ usb_GetImageProc( dev );
+
+ if( scan->sParam.bSource == SOURCE_ADF )
+ scan->dwFlag |= SCANFLAG_StillModule;
+
+ DBG( _DBG_INFO, "* scan->dwFlag=0x%08lx\n", scan->dwFlag );
+
+ if( !usb_ScanBegin( dev,
+ (scan->dwFlag&SCANFLAG_StillModule) ? SANE_FALSE:SANE_TRUE)) {
+
+ return _E_INTERNAL;
+ }
+
+ scan->dwFlag |= SCANFLAG_Scanning;
+
+ if( scan->sParam.UserDpi.y != scan->sParam.PhyDpi.y ) {
+
+ if( scan->sParam.UserDpi.y < scan->sParam.PhyDpi.y ) {
+
+ scan->wSumY = scan->sParam.PhyDpi.y - scan->sParam.UserDpi.y;
+ scan->dwFlag |= SCANFLAG_SampleY;
+ DBG( _DBG_INFO, "SampleY Flag set (%u != %u, wSumY=%u)\n",
+ scan->sParam.UserDpi.y, scan->sParam.PhyDpi.y, scan->wSumY );
+ }
+ }
+ }
+
+ dumpPicInit( &scan->sParam, "plustek-pic.raw" );
+
+ /* here the NT driver uses an extra reading thread...
+ * as the SANE stuff already forked the driver to read data, I think
+ * we should only read data by using a function...
+ */
+ scan->dwLinesUser = scan->sParam.Size.dwLines;
+ if( !scan->dwLinesUser )
+ return _E_BUFFER_TOO_SMALL;
+
+ if( scan->sParam.Size.dwLines < scan->dwLinesUser )
+ scan->dwLinesUser = scan->sParam.Size.dwLines;
+
+ scan->sParam.Size.dwLines -= scan->dwLinesUser;
+ if( scan->dwFlag & SCANFLAG_BottomUp )
+ scan->UserBuf.pb = buf + (scan->dwLinesUser - 1) * scan->dwBytesLine;
+ else
+ scan->UserBuf.pb = buf;
+
+ DBG(_DBG_INFO,"Reading the data now!\n" );
+ DBG(_DBG_INFO,"PhyDpi.x = %u\n", scan->sParam.PhyDpi.x );
+ DBG(_DBG_INFO,"PhyDpi.y = %u\n", scan->sParam.PhyDpi.y );
+ DBG(_DBG_INFO,"UserDpi.x = %u\n", scan->sParam.UserDpi.x );
+ DBG(_DBG_INFO,"UserDpi.y = %u\n", scan->sParam.UserDpi.y );
+ DBG(_DBG_INFO,"NumberOfScanBufs = %lu\n",scan->dwNumberOfScanBufs);
+ DBG(_DBG_INFO,"LinesPerScanBufs = %lu\n",scan->dwLinesPerScanBufs);
+ DBG(_DBG_INFO,"dwPhyBytes = %lu\n",scan->sParam.Size.dwPhyBytes);
+ DBG(_DBG_INFO,"dwPhyPixels = %lu\n",scan->sParam.Size.dwPhyPixels);
+ DBG(_DBG_INFO,"dwTotalBytes = %lu\n",scan->sParam.Size.dwTotalBytes);
+ DBG(_DBG_INFO,"dwPixels = %lu\n",scan->sParam.Size.dwPixels);
+ DBG(_DBG_INFO,"dwBytes = %lu\n",scan->sParam.Size.dwBytes);
+ DBG(_DBG_INFO,"dwValidPixels = %lu\n",scan->sParam.Size.dwValidPixels);
+ DBG(_DBG_INFO,"dwBytesScanBuf = %lu\n",scan->dwBytesScanBuf );
+ DBG(_DBG_INFO,"dwLinesDiscard = %lu\n",scan->dwLinesDiscard );
+ DBG(_DBG_INFO,"dwLinesToSkip = %u\n", scan->bLinesToSkip );
+ DBG(_DBG_INFO,"dwLinesUser = %lu\n",scan->dwLinesUser );
+ DBG(_DBG_INFO,"dwBytesLine = %lu\n",scan->dwBytesLine );
+
+ scan->pbGetDataBuf = scan->pbScanBufBegin;
+
+ scan->dwLinesToProcess = usb_ReadData( dev );
+ if( 0 == scan->dwLinesToProcess )
+ return _E_DATAREAD;
+
+ return 0;
+}
+
+/** as the name says, read one line...
+ */
+static int
+usbDev_ReadLine( Plustek_Device *dev )
+{
+ int wrap;
+ u_long cur;
+ ScanDef *scan = &dev->scanning;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ cur = scan->dwLinesUser;
+
+ /* we stay within this sample loop until one line has been processed for
+ * the user...
+ */
+ while( cur == scan->dwLinesUser ) {
+
+ if( usb_IsEscPressed()) {
+ DBG( _DBG_INFO, "readLine() - Cancel detected...\n" );
+ return _E_ABORT;
+ }
+
+ if( !(scan->dwFlag & SCANFLAG_SampleY)) {
+
+ scan->pfnProcess( dev );
+
+ /* Adjust user buffer pointer */
+ scan->UserBuf.pb += scan->lBufAdjust;
+ scan->dwLinesUser--;
+
+ } else {
+
+ scan->wSumY += scan->sParam.UserDpi.y;
+
+ if( scan->wSumY >= scan->sParam.PhyDpi.y ) {
+ scan->wSumY -= scan->sParam.PhyDpi.y;
+
+ scan->pfnProcess( dev );
+
+ /* Adjust user buffer pointer */
+ scan->UserBuf.pb += scan->lBufAdjust;
+ scan->dwLinesUser--;
+ }
+ }
+
+ /* Adjust get buffer pointers */
+ wrap = 0;
+
+ if( scan->sParam.bDataType == SCANDATATYPE_Color ) {
+
+ scan->Red.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Red.pb >= scan->pbScanBufEnd ) {
+ scan->Red.pb = scan->pbScanBufBegin + scan->dwRedShift;
+ wrap = 1;
+ }
+
+ scan->Green.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Green.pb >= scan->pbScanBufEnd ) {
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwGreenShift;
+ wrap = 1;
+ }
+
+ scan->Blue.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Blue.pb >= scan->pbScanBufEnd ) {
+ scan->Blue.pb = scan->pbScanBufBegin + scan->dwBlueShift;
+ wrap = 1;
+ }
+ } else {
+ scan->Green.pb += scan->sParam.Size.dwPhyBytes;
+ if( scan->Green.pb >= scan->pbScanBufEnd )
+ scan->Green.pb = scan->pbScanBufBegin + scan->dwGreenShift;
+ }
+
+ /* on any wrap-around of the get pointers in one channel mode
+ * we have to reset them
+ */
+ if( wrap ) {
+
+ u_long len = scan->sParam.Size.dwPhyBytes;
+
+ if( hw->bReg_0x26 & _ONE_CH_COLOR ) {
+
+ if(scan->sParam.bDataType == SCANDATATYPE_Color) {
+ len /= 3;
+ }
+ scan->Red.pb = scan->pbScanBufBegin;
+ scan->Green.pb = scan->pbScanBufBegin + len;
+ scan->Blue.pb = scan->pbScanBufBegin + len * 2UL;
+ }
+ }
+
+ /* line processed, check if we have to get more...
+ */
+ scan->dwLinesToProcess--;
+
+ if( 0 == scan->dwLinesToProcess ) {
+
+ scan->dwLinesToProcess = usb_ReadData( dev );
+ if( 0 == scan->dwLinesToProcess ) {
+
+ if( usb_IsEscPressed())
+ return _E_ABORT;
+ }
+ }
+ }
+ return 0;
+}
+
+/* END PLUSTEK-USB.C ........................................................*/