kopia lustrzana https://gitlab.com/sane-project/backends
4434 wiersze
131 KiB
C
4434 wiersze
131 KiB
C
/* sane - Scanner Access Now Easy.
|
|
|
|
Copyright (C) 2003 Oliver Rauch
|
|
Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
|
|
Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
|
|
Copyright (C) 2004 - 2006 Stephane Voltz <stefdev@modulonet.fr>
|
|
Copyright (C) 2005, 2006 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
|
|
|
|
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.
|
|
*/
|
|
|
|
#include "../include/sane/config.h"
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#include "../include/sane/sane.h"
|
|
#include "../include/sane/sanei.h"
|
|
#include "../include/sane/saneopts.h"
|
|
|
|
#include "../include/sane/sanei_backend.h"
|
|
#include "../include/sane/sanei_config.h"
|
|
#include "../include/sane/sanei_usb.h"
|
|
|
|
#include "genesys.h"
|
|
|
|
/* Individual bits */
|
|
#define REG01_CISSET 0x80
|
|
#define REG01_DOGENB 0x40
|
|
#define REG01_DVDSET 0x20
|
|
#define REG01_FASTMOD 0x10
|
|
#define REG01_COMPENB 0x08
|
|
#define REG01_DRAMSEL 0x04
|
|
#define REG01_SHDAREA 0x02
|
|
#define REG01_SCAN 0x01
|
|
|
|
#define REG02_NOTHOME 0x80
|
|
#define REG02_ACDCDIS 0x40
|
|
#define REG02_AGOHOME 0x20
|
|
#define REG02_MTRPWR 0x10
|
|
#define REG02_FASTFED 0x08
|
|
#define REG02_MTRREV 0x04
|
|
#define REG02_STEPSEL 0x03
|
|
|
|
#define REG02_FULLSTEP 0x00
|
|
#define REG02_HALFSTEP 0x01
|
|
#define REG02_QUATERSTEP 0x02
|
|
|
|
#define REG03_TG3 0x80
|
|
#define REG03_AVEENB 0x40
|
|
#define REG03_XPASEL 0x20
|
|
#define REG03_LAMPPWR 0x10
|
|
#define REG03_LAMPDOG 0x08
|
|
#define REG03_LAMPTIM 0x07
|
|
|
|
#define REG04_LINEART 0x80
|
|
#define REG04_BITSET 0x40
|
|
#define REG04_ADTYPE 0x30
|
|
#define REG04_FILTER 0x0c
|
|
#define REG04_FESET 0x03
|
|
|
|
#define REG05_DPIHW 0xc0
|
|
#define REG05_DPIHW_600 0x00
|
|
#define REG05_DPIHW_1200 0x40
|
|
#define REG05_DPIHW_2400 0x80
|
|
#define REG05_GMMTYPE 0x30
|
|
#define REG05_GMM14BIT 0x10
|
|
#define REG05_GMMENB 0x08
|
|
#define REG05_LEDADD 0x04
|
|
#define REG05_BASESEL 0x03
|
|
|
|
#define REG06_PWRBIT 0x10
|
|
#define REG06_GAIN4 0x08
|
|
#define REG06_OPTEST 0x07
|
|
|
|
#define REG07_DMASEL 0x02
|
|
#define REG07_DMARDWR 0x01
|
|
|
|
#define REG16_CTRLHI 0x80
|
|
#define REG16_SELINV 0x40
|
|
#define REG16_TGINV 0x20
|
|
#define REG16_CK1INV 0x10
|
|
#define REG16_CK2INV 0x08
|
|
#define REG16_CTRLINV 0x04
|
|
#define REG16_CKDIS 0x02
|
|
#define REG16_CTRLDIS 0x01
|
|
|
|
#define REG17_TGMODE 0xc0
|
|
#define REG17_TGMODE_NO_DUMMY 0x00
|
|
#define REG17_TGMODE_REF 0x40
|
|
#define REG17_TGMODE_XPA 0x80
|
|
#define REG17_TGW 0x3f
|
|
|
|
#define REG18_CNSET 0x80
|
|
#define REG18_DCKSEL 0x60
|
|
#define REG18_CKTOGGLE 0x10
|
|
#define REG18_CKDELAY 0x0c
|
|
#define REG18_CKSEL 0x03
|
|
|
|
#define REG1D_CKMANUAL 0x80
|
|
|
|
#define REG1E_WDTIME 0xf0
|
|
#define REG1E_LINESEL 0x0f
|
|
|
|
#define REG41_PWRBIT 0x80
|
|
#define REG41_BUFEMPTY 0x40
|
|
#define REG41_FEEDFSH 0x20
|
|
#define REG41_SCANFSH 0x10
|
|
#define REG41_HOMESNR 0x08
|
|
#define REG41_LAMPSTS 0x04
|
|
#define REG41_FEBUSY 0x02
|
|
#define REG41_MOTMFLG 0x01
|
|
|
|
#define REG66_LOW_CURRENT 0x10
|
|
|
|
#define REG6A_FSTPSEL 0xc0
|
|
#define REG6A_FASTPWM 0x3f
|
|
|
|
#define REG6C_TGTIME 0xc0
|
|
#define REG6C_Z1MOD 0x38
|
|
#define REG6C_Z2MOD 0x07
|
|
|
|
/* we don't need a genesys_gl646.h yet, declarations aren't numerous enough */
|
|
/* writable registers */
|
|
enum
|
|
{
|
|
reg_0x01 = 0,
|
|
reg_0x02,
|
|
reg_0x03,
|
|
reg_0x04,
|
|
reg_0x05,
|
|
reg_0x06,
|
|
reg_0x07,
|
|
reg_0x08,
|
|
reg_0x09,
|
|
reg_0x0a,
|
|
reg_0x0b,
|
|
reg_0x10,
|
|
reg_0x11,
|
|
reg_0x12,
|
|
reg_0x13,
|
|
reg_0x14,
|
|
reg_0x15,
|
|
reg_0x16,
|
|
reg_0x17,
|
|
reg_0x18,
|
|
reg_0x19,
|
|
reg_0x1a,
|
|
reg_0x1b,
|
|
reg_0x1c,
|
|
reg_0x1d,
|
|
reg_0x1e,
|
|
reg_0x1f,
|
|
reg_0x20,
|
|
reg_0x21,
|
|
reg_0x22,
|
|
reg_0x23,
|
|
reg_0x24,
|
|
reg_0x25,
|
|
reg_0x26,
|
|
reg_0x27,
|
|
reg_0x28,
|
|
reg_0x29,
|
|
reg_0x2c,
|
|
reg_0x2d,
|
|
reg_0x2e,
|
|
reg_0x2f,
|
|
reg_0x30,
|
|
reg_0x31,
|
|
reg_0x32,
|
|
reg_0x33,
|
|
reg_0x34,
|
|
reg_0x35,
|
|
reg_0x36,
|
|
reg_0x37,
|
|
reg_0x38,
|
|
reg_0x39,
|
|
reg_0x3d,
|
|
reg_0x3e,
|
|
reg_0x3f,
|
|
reg_0x52,
|
|
reg_0x53,
|
|
reg_0x54,
|
|
reg_0x55,
|
|
reg_0x56,
|
|
reg_0x57,
|
|
reg_0x58,
|
|
reg_0x59,
|
|
reg_0x5a,
|
|
reg_0x5b,
|
|
reg_0x5c,
|
|
reg_0x5d,
|
|
reg_0x5e,
|
|
reg_0x60,
|
|
reg_0x61,
|
|
reg_0x62,
|
|
reg_0x63,
|
|
reg_0x64,
|
|
reg_0x65,
|
|
reg_0x66,
|
|
reg_0x67,
|
|
reg_0x68,
|
|
reg_0x69,
|
|
reg_0x6a,
|
|
reg_0x6b,
|
|
reg_0x6c,
|
|
reg_0x6d,
|
|
GENESYS_GL646_MAX_REGS
|
|
};
|
|
|
|
/* Write to many registers */
|
|
static SANE_Status
|
|
gl646_bulk_write_register (Genesys_Device * dev,
|
|
Genesys_Register_Set * reg, size_t size)
|
|
{
|
|
SANE_Status status;
|
|
u_int8_t outdata[8];
|
|
u_int8_t buffer[GENESYS_MAX_REGS * 2];
|
|
unsigned int i;
|
|
|
|
/* handle differently sized register sets, reg[0x00] may be the last one */
|
|
i = 0;
|
|
while ((i < size / 2) && (reg[i].address != 0))
|
|
i++;
|
|
size = i * 2;
|
|
|
|
DBG (DBG_io, "gl646_bulk_write_register (size = %lu)\n", (u_long) size);
|
|
|
|
|
|
outdata[0] = BULK_OUT;
|
|
outdata[1] = BULK_REGISTER;
|
|
outdata[2] = 0x00;
|
|
outdata[3] = 0x00;
|
|
outdata[4] = (size & 0xff);
|
|
outdata[5] = ((size >> 8) & 0xff);
|
|
outdata[6] = ((size >> 16) & 0xff);
|
|
outdata[7] = ((size >> 24) & 0xff);
|
|
|
|
status =
|
|
sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
|
|
VALUE_BUFFER, INDEX, sizeof (outdata), outdata);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_write_register: failed while writing command: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* copy registers and values in data buffer */
|
|
for (i = 0; i < size; i += 2)
|
|
{
|
|
buffer[i] = reg[i / 2].address;
|
|
buffer[i + 1] = reg[i / 2].value;
|
|
}
|
|
|
|
status = sanei_usb_write_bulk (dev->dn, buffer, &size);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_write_register: failed while writing bulk data: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < size; i += 2)
|
|
DBG (DBG_io2, "reg[0x%02x] = 0x%02x\n", buffer[i], buffer[i + 1]);
|
|
|
|
DBG (DBG_io, "gl646_bulk_write_register: wrote %d bytes\n", size);
|
|
return status;
|
|
}
|
|
|
|
/* Write bulk data (e.g. shading, gamma) */
|
|
static SANE_Status
|
|
gl646_bulk_write_data (Genesys_Device * dev, u_int8_t addr,
|
|
u_int8_t * data, size_t len)
|
|
{
|
|
SANE_Status status;
|
|
size_t size;
|
|
u_int8_t outdata[8];
|
|
|
|
DBG (DBG_io, "gl646_bulk_write_data writing %lu bytes\n", (u_long) len);
|
|
|
|
status =
|
|
sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
|
|
VALUE_SET_REGISTER, INDEX, 1, &addr);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_write_data failed while setting register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
while (len)
|
|
{
|
|
if (len > BULKOUT_MAXSIZE)
|
|
size = BULKOUT_MAXSIZE;
|
|
else
|
|
size = len;
|
|
|
|
outdata[0] = BULK_OUT;
|
|
outdata[1] = BULK_RAM;
|
|
outdata[2] = 0x00;
|
|
outdata[3] = 0x00;
|
|
outdata[4] = (size & 0xff);
|
|
outdata[5] = ((size >> 8) & 0xff);
|
|
outdata[6] = ((size >> 16) & 0xff);
|
|
outdata[7] = ((size >> 24) & 0xff);
|
|
|
|
status =
|
|
sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
|
|
VALUE_BUFFER, INDEX, sizeof (outdata),
|
|
outdata);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_write_data failed while writing command: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sanei_usb_write_bulk (dev->dn, data, &size);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_write_data failed while writing bulk data: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_io2,
|
|
"gl646_bulk_write_data wrote %lu bytes, %lu remaining\n",
|
|
(u_long) size, (u_long) (len - size));
|
|
|
|
len -= size;
|
|
data += size;
|
|
}
|
|
|
|
DBG (DBG_io, "gl646_bulk_write_data: completed\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Read bulk data (e.g. scanned data) */
|
|
static SANE_Status
|
|
gl646_bulk_read_data (Genesys_Device * dev, u_int8_t addr,
|
|
u_int8_t * data, size_t len)
|
|
{
|
|
SANE_Status status;
|
|
size_t size;
|
|
u_int8_t outdata[8];
|
|
|
|
DBG (DBG_io, "gl646_bulk_read_data: requesting %lu bytes\n", (u_long) len);
|
|
|
|
status =
|
|
sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
|
|
VALUE_SET_REGISTER, INDEX, 1, &addr);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_read_data failed while setting register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
outdata[0] = BULK_IN;
|
|
outdata[1] = BULK_RAM;
|
|
outdata[2] = 0x00;
|
|
outdata[3] = 0x00;
|
|
outdata[4] = (len & 0xff);
|
|
outdata[5] = ((len >> 8) & 0xff);
|
|
outdata[6] = ((len >> 16) & 0xff);
|
|
outdata[7] = ((len >> 24) & 0xff);
|
|
|
|
status =
|
|
sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
|
|
VALUE_BUFFER, INDEX, sizeof (outdata), outdata);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_read_data failed while writing command: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
while (len)
|
|
{
|
|
if (len > BULKIN_MAXSIZE)
|
|
size = BULKIN_MAXSIZE;
|
|
else
|
|
size = len;
|
|
|
|
DBG (DBG_io2,
|
|
"gl646_bulk_read_data: trying to read %lu bytes of data\n",
|
|
(u_long) size);
|
|
status = sanei_usb_read_bulk (dev->dn, data, &size);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_bulk_read_data failed while reading bulk data: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_io2,
|
|
"gl646_bulk_read_data read %lu bytes, %lu remaining\n",
|
|
(u_long) size, (u_long) (len - size));
|
|
|
|
len -= size;
|
|
data += size;
|
|
}
|
|
|
|
DBG (DBG_io, "gl646_bulk_read_data: completed\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_get_fast_feed_bit (Genesys_Register_Set * regs)
|
|
{
|
|
Genesys_Register_Set *r = NULL;
|
|
|
|
r = sanei_genesys_get_address (regs, 0x02);
|
|
if (r && (r->value & REG02_FASTFED))
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_get_filter_bit (Genesys_Register_Set * regs)
|
|
{
|
|
Genesys_Register_Set *r = NULL;
|
|
|
|
r = sanei_genesys_get_address (regs, 0x04);
|
|
if (r && (r->value & REG04_FILTER))
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_get_lineart_bit (Genesys_Register_Set * regs)
|
|
{
|
|
Genesys_Register_Set *r = NULL;
|
|
|
|
r = sanei_genesys_get_address (regs, 0x04);
|
|
if (r && (r->value & REG04_LINEART))
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_get_bitset_bit (Genesys_Register_Set * regs)
|
|
{
|
|
Genesys_Register_Set *r = NULL;
|
|
|
|
r = sanei_genesys_get_address (regs, 0x04);
|
|
if (r && (r->value & REG04_BITSET))
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_get_gain4_bit (Genesys_Register_Set * regs)
|
|
{
|
|
Genesys_Register_Set *r = NULL;
|
|
|
|
r = sanei_genesys_get_address (regs, 0x06);
|
|
if (r && (r->value & REG06_GAIN4))
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_test_buffer_empty_bit (SANE_Byte val)
|
|
{
|
|
if (val & REG41_BUFEMPTY)
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static SANE_Bool
|
|
gl646_test_motor_flag_bit (SANE_Byte val)
|
|
{
|
|
if (val & REG41_MOTMFLG)
|
|
return SANE_TRUE;
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
/*
|
|
* sets r21 to r23 registers, ie number of steps for motor
|
|
* todo : some hardcoded values will be put in Motor_Struct
|
|
*/
|
|
static void
|
|
gl646_setup_steps (Genesys_Device * dev, Genesys_Register_Set * regs, int dpi)
|
|
{
|
|
Genesys_Register_Set *r;
|
|
int fw, bw;
|
|
|
|
if (regs[reg_0x04].value & REG04_FILTER)
|
|
{
|
|
/* monochrome */
|
|
switch (dev->model->motor_type)
|
|
{
|
|
case MOTOR_HP2300:
|
|
case MOTOR_HP2400:
|
|
if (dpi <= 75)
|
|
fw = 120;
|
|
else if (dpi <= 150)
|
|
fw = 67;
|
|
else if (dpi <= 300)
|
|
fw = 44;
|
|
else
|
|
fw = 3;
|
|
bw = 16;
|
|
break;
|
|
case MOTOR_5345:
|
|
default:
|
|
if (dpi <= 600)
|
|
fw = 255;
|
|
else
|
|
fw = 32;
|
|
bw = 64;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (dev->model->motor_type)
|
|
{
|
|
case MOTOR_HP2300:
|
|
case MOTOR_HP2400:
|
|
if (dpi <= 75)
|
|
fw = 120;
|
|
else if (dpi <= 150)
|
|
fw = 67;
|
|
else if (dpi <= 300)
|
|
fw = 44;
|
|
else
|
|
fw = 3;
|
|
bw = 16;
|
|
break;
|
|
|
|
case MOTOR_5345:
|
|
default:
|
|
if (dpi < 200)
|
|
{
|
|
fw = 255;
|
|
bw = 64;
|
|
}
|
|
else if (dpi < 400)
|
|
{
|
|
fw = 64;
|
|
bw = 32;
|
|
}
|
|
else if (dpi <= 600)
|
|
{
|
|
fw = 32;
|
|
bw = 32;
|
|
}
|
|
else
|
|
{
|
|
fw = 16;
|
|
bw = 146;
|
|
}
|
|
}
|
|
}
|
|
|
|
r = sanei_genesys_get_address (regs, 0x21);
|
|
r->value = fw;
|
|
r = sanei_genesys_get_address (regs, 0x22);
|
|
r->value = bw;
|
|
r = sanei_genesys_get_address (regs, 0x23);
|
|
r->value = bw;
|
|
r = sanei_genesys_get_address (regs, 0x24);
|
|
r->value = fw;
|
|
}
|
|
|
|
/** copy sensor specific settings */
|
|
/* *dev : device infos
|
|
*regs : regiters to be set
|
|
extended : do extebded set up
|
|
half_ccd: set up for half ccd resolution
|
|
all registers 08-0B, 10-1D, 52-59 are set up. They shouldn't
|
|
appear anywhere else but in register_ini
|
|
*/
|
|
static void
|
|
gl646_setup_sensor (Genesys_Device * dev,
|
|
Genesys_Register_Set * regs,
|
|
SANE_Bool extended, SANE_Bool half_ccd)
|
|
{
|
|
Genesys_Register_Set *r;
|
|
int i;
|
|
|
|
r = sanei_genesys_get_address (regs, 0x08);
|
|
for (i = 0; i < 4; i++, r++)
|
|
r->value = dev->sensor.regs_0x08_0x0b[i];
|
|
|
|
r = sanei_genesys_get_address (regs, 0x10);
|
|
for (i = 0; i < 14; i++, r++)
|
|
r->value = dev->sensor.regs_0x10_0x1d[i];
|
|
|
|
r = sanei_genesys_get_address (regs, 0x52);
|
|
for (i = 0; i < 13; i++, r++)
|
|
r->value = dev->sensor.regs_0x52_0x5e[i];
|
|
|
|
/* don't go nay further if no extended setup */
|
|
if (!extended)
|
|
return;
|
|
|
|
/* todo : add more CCD types if needed */
|
|
/* we might want to expand the Sensor struct to have these
|
|
2 kind of settings */
|
|
if (dev->model->ccd_type == CCD_5345)
|
|
{
|
|
if (half_ccd)
|
|
{
|
|
/* settings for CCD used at half is max resolution */
|
|
r = sanei_genesys_get_address (regs, 0x08);
|
|
r->value = 0x00;
|
|
r = sanei_genesys_get_address (regs, 0x09);
|
|
r->value = 0x05;
|
|
r = sanei_genesys_get_address (regs, 0x0a);
|
|
r->value = 0x06;
|
|
r = sanei_genesys_get_address (regs, 0x0b);
|
|
r->value = 0x08;
|
|
r = sanei_genesys_get_address (regs, 0x18);
|
|
r->value = 0x28;
|
|
r = sanei_genesys_get_address (regs, 0x58);
|
|
r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
|
|
}
|
|
else
|
|
{
|
|
/* swap latch times */
|
|
r = sanei_genesys_get_address (regs, 0x18);
|
|
r->value = 0x30;
|
|
r = sanei_genesys_get_address (regs, 0x52);
|
|
for (i = 0; i < 6; i++, r++)
|
|
r->value = dev->sensor.regs_0x52_0x5e[(i + 3) % 6];
|
|
r = sanei_genesys_get_address (regs, 0x58);
|
|
r->value = 0x20 | (r->value & 0x03); /* VSMP=4 */
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (dev->model->ccd_type == CCD_HP2300)
|
|
{
|
|
/* settings for CCD used at half is max resolution */
|
|
if (half_ccd)
|
|
{
|
|
r = sanei_genesys_get_address (regs, 0x08);
|
|
r->value = 0x16;
|
|
r = sanei_genesys_get_address (regs, 0x09);
|
|
r->value = 0x00;
|
|
r = sanei_genesys_get_address (regs, 0x0a);
|
|
r->value = 0x01;
|
|
r = sanei_genesys_get_address (regs, 0x0b);
|
|
r->value = 0x03;
|
|
/* manual clock programming */
|
|
r = sanei_genesys_get_address (regs, 0x1d);
|
|
r->value |= 0x80;
|
|
}
|
|
else
|
|
{
|
|
r = sanei_genesys_get_address (regs, 0x08);
|
|
r->value = 1;
|
|
r = sanei_genesys_get_address (regs, 0x09);
|
|
r->value = 3;
|
|
r = sanei_genesys_get_address (regs, 0x0a);
|
|
r->value = 4;
|
|
r = sanei_genesys_get_address (regs, 0x0b);
|
|
r->value = 6;
|
|
}
|
|
r = sanei_genesys_get_address (regs, 0x58);
|
|
r->value = 0x80 | (r->value & 0x03); /* VSMP=16 */
|
|
return;
|
|
}
|
|
|
|
if (dev->model->ccd_type == CCD_HP2400)
|
|
{
|
|
/* settings for CCD used at half is max resolution */
|
|
if (half_ccd)
|
|
{
|
|
r = sanei_genesys_get_address (regs, 0x08);
|
|
r->value = 2;
|
|
r = sanei_genesys_get_address (regs, 0x09);
|
|
r->value = 4;
|
|
r = sanei_genesys_get_address (regs, 0x0a);
|
|
r->value = 0;
|
|
r = sanei_genesys_get_address (regs, 0x0b);
|
|
r->value = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Test if the ASIC works
|
|
*/
|
|
static SANE_Status
|
|
gl646_asic_test (Genesys_Device * dev)
|
|
{
|
|
SANE_Status status;
|
|
u_int8_t val;
|
|
u_int8_t *data;
|
|
u_int8_t *verify_data;
|
|
size_t size, verify_size;
|
|
unsigned int i;
|
|
|
|
DBG (DBG_proc, "gl646_asic_test\n");
|
|
|
|
/* set and read exposure time, compare if it's the same */
|
|
status = sanei_genesys_write_register (dev, 0x38, 0xde);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to write register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sanei_genesys_write_register (dev, 0x39, 0xad);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to write register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sanei_genesys_read_register (dev, 0x4e, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to read register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
if (val != 0xde) /* value of register 0x38 */
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: register contains invalid value\n");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
status = sanei_genesys_read_register (dev, 0x4f, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to read register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
if (val != 0xad) /* value of register 0x39 */
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: register contains invalid value\n");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
/* ram test: */
|
|
size = 0x40000;
|
|
verify_size = size + 0x80;
|
|
/* todo: looks like the read size must be a multiple of 128?
|
|
otherwise the read doesn't succeed the second time after the scanner has
|
|
been plugged in. Very strange. */
|
|
|
|
data = (u_int8_t *) malloc (size);
|
|
if (!data)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: could not allocate memory\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
verify_data = (u_int8_t *) malloc (verify_size);
|
|
if (!verify_data)
|
|
{
|
|
free (data);
|
|
DBG (DBG_error, "gl646_asic_test: could not allocate memory\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
for (i = 0; i < (size - 1); i += 2)
|
|
{
|
|
data[i] = i / 512;
|
|
data[i + 1] = (i / 2) % 256;
|
|
}
|
|
|
|
status = sanei_genesys_set_buffer_address (dev, 0x0000);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to set buffer address: %s\n",
|
|
sane_strstatus (status));
|
|
free (data);
|
|
free (verify_data);
|
|
return status;
|
|
}
|
|
|
|
status = gl646_bulk_write_data (dev, 0x3c, data, size);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to bulk write data: %s\n",
|
|
sane_strstatus (status));
|
|
free (data);
|
|
free (verify_data);
|
|
return status;
|
|
}
|
|
|
|
status = sanei_genesys_set_buffer_address (dev, 0x0000);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to set buffer address: %s\n",
|
|
sane_strstatus (status));
|
|
free (data);
|
|
free (verify_data);
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_bulk_read_data (dev, 0x45, (u_int8_t *) verify_data, verify_size);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: failed to bulk read data: %s\n",
|
|
sane_strstatus (status));
|
|
free (data);
|
|
free (verify_data);
|
|
return status;
|
|
}
|
|
|
|
/* todo: why i + 2 ? */
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
if (verify_data[i + 2] != data[i])
|
|
{
|
|
DBG (DBG_error, "gl646_asic_test: data verification error\n");
|
|
free (data);
|
|
free (verify_data);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
}
|
|
|
|
free (data);
|
|
free (verify_data);
|
|
|
|
DBG (DBG_info, "gl646_asic_test: completed\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* returns the max register bulk size */
|
|
static int
|
|
gl646_bulk_full_size (void)
|
|
{
|
|
return GENESYS_GL646_MAX_REGS;
|
|
}
|
|
|
|
/*
|
|
* Set all registers to default values
|
|
*/
|
|
static void
|
|
gl646_init_regs (Genesys_Device * dev)
|
|
{
|
|
int nr, addr, i;
|
|
Genesys_Register_Set *r = NULL;
|
|
|
|
DBG (DBG_proc, "gl646_init_regs\n");
|
|
|
|
nr = 0;
|
|
memset (dev->reg, 0, GENESYS_MAX_REGS * sizeof (Genesys_Register_Set));
|
|
|
|
for (addr = 1; addr <= 0x0b; addr++)
|
|
dev->reg[nr++].address = addr;
|
|
for (addr = 0x10; addr <= 0x29; addr++)
|
|
dev->reg[nr++].address = addr;
|
|
for (addr = 0x2c; addr <= 0x39; addr++)
|
|
dev->reg[nr++].address = addr;
|
|
for (addr = 0x3d; addr <= 0x3f; addr++)
|
|
dev->reg[nr++].address = addr;
|
|
for (addr = 0x52; addr <= 0x5e; addr++)
|
|
dev->reg[nr++].address = addr;
|
|
for (addr = 0x60; addr <= 0x6d; addr++)
|
|
dev->reg[nr++].address = addr;
|
|
|
|
/* ST12: 0x01 0x20 0x02 0x30 0x03 0x1f 0x04 0x13 0x05 0x10 */
|
|
/* ST24: 0x01 0x20 0x02 0x30 0x03 0x1f 0x04 0x13 0x05 0x50 */
|
|
dev->reg[reg_0x01].value = 0x20 /*0x22 */ ; /* enable shading, CCD, color, 1M */
|
|
dev->reg[reg_0x02].value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
dev->reg[reg_0x02].value |= 0x01; /* half-step */
|
|
dev->reg[reg_0x03].value = 0x1f /*0x17 */ ; /* lamp on */
|
|
dev->reg[reg_0x04].value = 0x13 /*0x03 */ ; /* 8 bits data, 16 bits A/D, color, Wolfson fe *//* todo: according to spec, 0x0 is reserved? */
|
|
|
|
dev->reg[reg_0x05].value = 0x00; /* 12 bits gamma, disable gamma, 24 clocks/pixel */
|
|
switch (dev->sensor.optical_res)
|
|
{
|
|
case 600:
|
|
dev->reg[reg_0x05].value |= REG05_DPIHW_600;
|
|
break;
|
|
case 1200:
|
|
dev->reg[reg_0x05].value |= REG05_DPIHW_1200;
|
|
break;
|
|
case 2400:
|
|
dev->reg[reg_0x05].value |= REG05_DPIHW_2400;
|
|
break;
|
|
}
|
|
if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA)
|
|
dev->reg[reg_0x05].value |= REG05_GMM14BIT;
|
|
|
|
if (dev->model->ccd_type == CCD_HP2300)
|
|
dev->reg[reg_0x06].value = 0x00; /* PWRBIT off, shading gain=4, normal AFE image capture */
|
|
else
|
|
dev->reg[reg_0x06].value = 0x18; /* PWRBIT on, shading gain=8, normal AFE image capture */
|
|
|
|
|
|
gl646_setup_sensor (dev, dev->reg, 0, 0);
|
|
|
|
dev->reg[reg_0x1e].value = 0xf0; /* watch-dog time */
|
|
|
|
/* ST12: 0x20 0x10 0x21 0x08 0x22 0x10 0x23 0x10 0x24 0x08 0x25 0x00 0x26 0x00 0x27 0xd4 0x28 0x01 0x29 0xff */
|
|
/* ST24: 0x20 0x10 0x21 0x08 0x22 0x10 0x23 0x10 0x24 0x08 0x25 0x00 0x26 0x00 0x27 0xd4 0x28 0x01 0x29 0xff */
|
|
|
|
if (dev->model->ccd_type != CCD_HP2300)
|
|
{
|
|
dev->reg[reg_0x1f].value = 0x01; /* scan feed step for table one in two table mode only */
|
|
dev->reg[reg_0x20].value = 0x10 /*0x01 */ ; /* n * 2k, below this condition, motor move forward *//* todo: huh, 2k is pretty low? */
|
|
}
|
|
else
|
|
{
|
|
dev->reg[reg_0x1e].value = 0xf0;
|
|
dev->reg[reg_0x1f].value = 0x10;
|
|
dev->reg[reg_0x20].value = 0x20;
|
|
}
|
|
dev->reg[reg_0x21].value = 0x08 /*0x20 */ ; /* table one steps number for forward slope curve of the acc/dec */
|
|
dev->reg[reg_0x22].value = 0x10 /*0x08 */ ; /* steps number of the forward steps for start/stop */
|
|
dev->reg[reg_0x23].value = 0x10 /*0x08 */ ; /* steps number of the backward steps for start/stop */
|
|
dev->reg[reg_0x24].value = 0x08 /*0x20 */ ; /* table one steps number backward slope curve of the acc/dec */
|
|
dev->reg[reg_0x25].value = 0x00; /* scan line numbers (7000) */
|
|
dev->reg[reg_0x26].value = 0x00 /*0x1b */ ;
|
|
dev->reg[reg_0x27].value = 0xd4 /*0x58 */ ;
|
|
dev->reg[reg_0x28].value = 0x01; /* PWM duty for lamp control */
|
|
dev->reg[reg_0x29].value = 0xff;
|
|
|
|
/* ST12: 0x2c 0x02 0x2d 0x58 0x2e 0x6e 0x2f 0x80 */
|
|
/* ST24: 0x2c 0x02 0x2d 0x58 0x2e 0x6e 0x2f 0x80 */
|
|
|
|
dev->reg[reg_0x2c].value = 0x02; /* set resolution (600 DPI) */
|
|
dev->reg[reg_0x2d].value = 0x58;
|
|
dev->reg[reg_0x2e].value = 0x78; /* set black&white threshold high level */
|
|
dev->reg[reg_0x2f].value = 0x7f; /* set black&white threshold low level */
|
|
if (dev->model->ccd_type == CCD_5345)
|
|
{
|
|
dev->calib_reg[reg_0x2e].value = 0x6e;
|
|
dev->calib_reg[reg_0x2f].value = 0x6e;
|
|
}
|
|
|
|
/* ST12: 0x30 0x00 0x31 0x0e 0x32 0x17 0x33 0x70 0x34 0x0c 0x35 0x01 0x36 0x00 0x37 0x00 0x38 0x17 0x39 0x70 */
|
|
/* ST24: 0x30 0x00 0x31 0x0c 0x32 0x2a 0x33 0xf8 0x34 0x0c 0x35 0x01 0x36 0x00 0x37 0x00 0x38 0x2a 0x39 0xf8 */
|
|
dev->reg[reg_0x30].value = 0x00; /* begin pixel positon (16) */
|
|
dev->reg[reg_0x31].value = dev->sensor.dummy_pixel /*0x10 */ ; /* TGW + 2*TG_SHLD + x */
|
|
dev->reg[reg_0x32].value = 0x2a /*0x15 */ ; /* end pixel position (5390) */
|
|
dev->reg[reg_0x33].value = 0xf8 /*0x0e */ ; /* TGW + 2*TG_SHLD + y */
|
|
dev->reg[reg_0x34].value = dev->sensor.dummy_pixel;
|
|
dev->reg[reg_0x35].value = 0x01 /*0x00 */ ; /* set maximum word size per line, for buffer full control (10800) */
|
|
dev->reg[reg_0x36].value = 0x00 /*0x2a */ ;
|
|
dev->reg[reg_0x37].value = 0x00 /*0x30 */ ;
|
|
dev->reg[reg_0x38].value = HIBYTE (dev->settings.exposure_time) /*0x2a */ ; /* line period (exposure time = 11000 pixels) */
|
|
dev->reg[reg_0x39].value = LOBYTE (dev->settings.exposure_time) /*0xf8 */ ;
|
|
|
|
/* ST12: 0x3d 0x00 0x3e 0x00 0x3f 0x01 */
|
|
/* ST24: 0x3d 0x00 0x3e 0x00 0x3f 0x01 */
|
|
dev->reg[reg_0x3d].value = 0x00; /* set feed steps number of motor move */
|
|
dev->reg[reg_0x3e].value = 0x00;
|
|
dev->reg[reg_0x3f].value = 0x01 /*0x00 */ ;
|
|
|
|
#if 0
|
|
/* ST12: 0x52 0x0f 0x53 0x13 0x54 0x17 0x55 0x03 0x56 0x07 0x57 0x0b 0x58 0x83 0x59 0x00 */
|
|
/* ST24: 0x52 0x0f 0x53 0x13 0x54 0x17 0x55 0x03 0x56 0x07 0x57 0x0b 0x58 0x83 0x59 0x00 */
|
|
dev->reg[reg_0x52].value = 0x0f /*0x13 */ ; /* R position */
|
|
dev->reg[reg_0x53].value = 0x13 /*0x17 */ ;
|
|
dev->reg[reg_0x54].value = 0x17 /*0x03 */ ; /* G position */
|
|
dev->reg[reg_0x55].value = 0x03 /*0x07 */ ;
|
|
dev->reg[reg_0x56].value = 0x07 /*0x0b */ ; /* B position on AFE signal */
|
|
dev->reg[reg_0x57].value = 0x0b /*0x0f */ ;
|
|
dev->reg[reg_0x58].value = 0x83 /*0x23 */ ; /* set rising edge and width of image sample for AFE */
|
|
dev->reg[reg_0x59].value = 0x00; /* set rising edge and width of dark voltage position */
|
|
#endif
|
|
|
|
for (i = 0; i < 6; i++, r++)
|
|
{
|
|
r = sanei_genesys_get_address (dev->reg, 0x52 + i);
|
|
r->value = dev->sensor.regs_0x52_0x5e[i];
|
|
}
|
|
|
|
/* ST12: 0x5a 0xc1 0x5b 0x00 0x5c 0x00 0x5d 0x00 0x5e 0x00 */
|
|
/* ST24: 0x5a 0xc1 0x5b 0x00 0x5c 0x00 0x5d 0x00 0x5e 0x00 */
|
|
dev->reg[reg_0x5a].value = 0xc1; /* Wolfson AFE, reset level clamp on pixel, RLC of AFE for line rate scanning type */
|
|
dev->reg[reg_0x5b].value = 0x00; /* First point of rising edge */
|
|
dev->reg[reg_0x5c].value = 0x00; /* First point of falling edge */
|
|
dev->reg[reg_0x5d].value = 0x00; /* Second point of rising edge */
|
|
dev->reg[reg_0x5e].value = 0x00; /* Second point of falling edge */
|
|
|
|
/* ST12: 0x60 0x00 0x61 0x00 0x62 0x00 0x63 0x00 0x64 0x00 0x65 0x00 0x66 0x00 0x67 0x00 0x68 0x51 0x69 0x20 */
|
|
/* ST24: 0x60 0x00 0x61 0x00 0x62 0x00 0x63 0x00 0x64 0x00 0x65 0x00 0x66 0x00 0x67 0x00 0x68 0x51 0x69 0x20 */
|
|
dev->reg[reg_0x60].value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
|
|
dev->reg[reg_0x61].value = 0x00; /* (21h+22h)/LPeriod */
|
|
dev->reg[reg_0x62].value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
|
|
dev->reg[reg_0x63].value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
|
|
dev->reg[reg_0x64].value = 0x00; /* motor PWM frequency */
|
|
dev->reg[reg_0x65].value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
dev->reg[reg_0x65].value = 0x02; /* PWM duty cycle for table one motor phase (63 = max) */
|
|
dev->reg[reg_0x66].value = dev->gpo.value[0];
|
|
dev->reg[reg_0x67].value = dev->gpo.value[1];
|
|
dev->reg[reg_0x68].value = dev->gpo.enable[0];
|
|
dev->reg[reg_0x69].value = dev->gpo.enable[1];
|
|
|
|
/* ST12: 0x6a 0x7f 0x6b 0xff 0x6c 0x00 0x6d 0x01 */
|
|
/* ST24: 0x6a 0x40 0x6b 0xff 0x6c 0x00 0x6d 0x01 */
|
|
if (dev->model->motor_type == MOTOR_HP2300)
|
|
{
|
|
dev->reg[reg_0x6a].value = 0x7f; /* table two steps number for acc/dec */
|
|
dev->reg[reg_0x6b].value = 0x78; /* table two steps number for acc/dec */
|
|
}
|
|
else
|
|
{
|
|
dev->reg[reg_0x6b].value = 0xff; /* table two steps number for acc/dec */
|
|
dev->reg[reg_0x6a].value = 0x40; /* table two fast moving step type, PWM duty for table two */
|
|
}
|
|
dev->reg[reg_0x6c].value = 0x00; /* peroid times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
|
|
dev->reg[reg_0x6d].value = 0x01 /*0x1f */ ; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
dev->reg[reg_0x6a].value = 0x42; /* table two fast moving step type, PWM duty for table two */
|
|
dev->reg[reg_0x6d].value = 0x41 /*0x1f */ ; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
|
|
}
|
|
}
|
|
|
|
|
|
/* Send slope table for motor movement
|
|
slope_table in machine byte order
|
|
*/
|
|
static SANE_Status
|
|
gl646_send_slope_table (Genesys_Device * dev, int table_nr,
|
|
u_int16_t * slope_table, int steps)
|
|
{
|
|
int dpihw;
|
|
int start_address;
|
|
SANE_Status status;
|
|
u_int8_t *table;
|
|
#ifdef WORDS_BIGENDIAN
|
|
int i;
|
|
#endif
|
|
|
|
DBG (DBG_proc, "gl646_send_slope_table (table_nr = %d, steps = %d)\n",
|
|
table_nr, steps);
|
|
|
|
dpihw = dev->reg[reg_0x05].value >> 6;
|
|
|
|
if (dpihw == 0) /* 600 dpi */
|
|
start_address = 0x08000;
|
|
else if (dpihw == 1) /* 1200 dpi */
|
|
start_address = 0x10000;
|
|
else if (dpihw == 2) /* 2400 dpi */
|
|
start_address = 0x1f800;
|
|
else /* reserved */
|
|
return SANE_STATUS_INVAL;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
table = (u_int8_t *) malloc (steps * 2);
|
|
for (i = 0; i < steps; i++)
|
|
{
|
|
table[i * 2] = slope_table[i] & 0xff;
|
|
table[i * 2 + 1] = slope_table[i] >> 8;
|
|
}
|
|
#else
|
|
table = (u_int8_t *) slope_table;
|
|
#endif
|
|
|
|
status =
|
|
sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x100);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
free (table);
|
|
#endif
|
|
DBG (DBG_error,
|
|
"gl646_send_slope_table: failed to set buffer address: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = gl646_bulk_write_data (dev, 0x3c, (u_int8_t *) table, steps * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
free (table);
|
|
#endif
|
|
DBG (DBG_error,
|
|
"gl646_send_slope_table: failed to send slope table: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
free (table);
|
|
#endif
|
|
DBG (DBG_proc, "gl646_send_slope_table: completed\n");
|
|
return status;
|
|
}
|
|
|
|
|
|
/* Set values of analog frontend */
|
|
static SANE_Status
|
|
gl646_set_fe (Genesys_Device * dev, u_int8_t set)
|
|
{
|
|
SANE_Status status;
|
|
int i;
|
|
u_int8_t val;
|
|
|
|
DBG (DBG_proc, "gl646_set_fe (%s)\n",
|
|
set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set ==
|
|
AFE_POWER_SAVE ? "powersave" : "huh?");
|
|
|
|
if ((dev->reg[reg_0x04].value & REG04_FESET) != 0x03)
|
|
{
|
|
DBG (DBG_proc, "gl646_set_fe(): unspported frontend type %d\n",
|
|
dev->reg[reg_0x04].value & REG04_FESET);
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (set == AFE_INIT)
|
|
{
|
|
DBG (DBG_proc, "gl646_set_fe(): setting DAC %u\n",
|
|
dev->model->dac_type);
|
|
sanei_genesys_init_fe (dev);
|
|
|
|
/* reset only done on init */
|
|
status = sanei_genesys_fe_write_data (dev, 0x04, 0x80);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: reset fe failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
if (dev->model->ccd_type == CCD_HP2300
|
|
|| dev->model->ccd_type == CCD_HP2400)
|
|
{
|
|
val = 0x07;
|
|
status =
|
|
sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT,
|
|
REQUEST_REGISTER, GPIO_OUTPUT_ENABLE,
|
|
INDEX, 1, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_set_fe failed resetting frontend: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (set == AFE_POWER_SAVE)
|
|
{
|
|
status = sanei_genesys_fe_write_data (dev, 0x01, 0x02);
|
|
if (status != SANE_STATUS_GOOD)
|
|
DBG (DBG_error, "gl646_set_fe: writing data failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* todo : base this test on cfg reg3 or a CCD family flag to be created */
|
|
/*if (dev->model->ccd_type!=CCD_HP2300 && dev->model->ccd_type!=CCD_HP2400) */
|
|
{
|
|
status = sanei_genesys_fe_write_data (dev, 0x00, dev->frontend.reg[0]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: writing reg0 failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
status = sanei_genesys_fe_write_data (dev, 0x02, dev->frontend.reg[2]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: writing reg2 failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
status = sanei_genesys_fe_write_data (dev, 0x01, dev->frontend.reg[1]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: writing reg1 failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sanei_genesys_fe_write_data (dev, 0x03, dev->frontend.reg[3]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: writing reg3 failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
status =
|
|
sanei_genesys_fe_write_data (dev, 0x24 + i, dev->frontend.sign[i]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: writing sign[%d] failed: %s\n",
|
|
i, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
sanei_genesys_fe_write_data (dev, 0x28 + i, dev->frontend.gain[i]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_set_fe: writing gain[%d] failed: %s\n",
|
|
i, sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
sanei_genesys_fe_write_data (dev, 0x20 + i, dev->frontend.offset[i]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_set_fe: writing offset[%d] failed: %s\n", i,
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_set_fe: completed\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
static void
|
|
gl646_set_motor_power (Genesys_Register_Set * regs, SANE_Bool set)
|
|
{
|
|
if (set)
|
|
{
|
|
sanei_genesys_set_reg_from_set (regs, 0x02,
|
|
sanei_genesys_read_reg_from_set (regs,
|
|
0x02) |
|
|
REG02_MTRPWR);
|
|
}
|
|
else
|
|
{
|
|
sanei_genesys_set_reg_from_set (regs, 0x02,
|
|
sanei_genesys_read_reg_from_set (regs,
|
|
0x02) &
|
|
~REG02_MTRPWR);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gl646_set_lamp_power (Genesys_Register_Set * regs, SANE_Bool set)
|
|
{
|
|
if (set)
|
|
{
|
|
sanei_genesys_set_reg_from_set (regs, 0x03,
|
|
sanei_genesys_read_reg_from_set (regs,
|
|
0x03) |
|
|
REG03_LAMPPWR);
|
|
}
|
|
else
|
|
{
|
|
sanei_genesys_set_reg_from_set (regs, 0x03,
|
|
sanei_genesys_read_reg_from_set (regs,
|
|
0x03) &
|
|
~REG03_LAMPPWR);
|
|
}
|
|
}
|
|
|
|
static SANE_Status
|
|
gl646_save_power (Genesys_Device * dev, SANE_Bool enable)
|
|
{
|
|
|
|
DBG (DBG_proc, "gl646_save_power: enable = %d\n", enable);
|
|
|
|
if (enable)
|
|
{
|
|
/* gl646_set_fe (dev, AFE_POWER_SAVE); */
|
|
}
|
|
else
|
|
{
|
|
gl646_set_fe (dev, AFE_INIT);
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
static SANE_Status
|
|
gl646_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ )
|
|
{
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
Genesys_Register_Set local_reg[6];
|
|
int rate, exposure_time, tgtime, time;
|
|
|
|
DBG (DBG_proc, "gl646_set_powersaving (delay = %d)\n", delay);
|
|
|
|
local_reg[0].address = 0x01;
|
|
local_reg[0].value = sanei_genesys_read_reg_from_set (dev->reg, 0x01); /* disable fastmode */
|
|
|
|
local_reg[1].address = 0x03;
|
|
local_reg[1].value = sanei_genesys_read_reg_from_set (dev->reg, 0x03); /* Lamp power control */
|
|
|
|
local_reg[2].address = 0x05;
|
|
local_reg[2].value = sanei_genesys_read_reg_from_set (dev->reg, 0x05) & ~REG05_BASESEL; /* 24 clocks/pixel */
|
|
|
|
local_reg[3].address = 0x38; /* line period low */
|
|
local_reg[3].value = 0x00;
|
|
|
|
local_reg[4].address = 0x39; /* line period high */
|
|
local_reg[4].value = 0x00;
|
|
|
|
local_reg[5].address = 0x6c; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE */
|
|
local_reg[5].value = 0x00;
|
|
|
|
if (!delay)
|
|
local_reg[1].value = local_reg[1].value & 0xf0; /* disable lampdog and set lamptime = 0 */
|
|
else if (delay < 20)
|
|
local_reg[1].value = (local_reg[1].value & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */
|
|
else
|
|
local_reg[1].value = (local_reg[1].value & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */
|
|
|
|
time = delay * 1000 * 60; /* -> msec */
|
|
exposure_time =
|
|
(u_int32_t) (time * 32000.0 /
|
|
(24.0 * 64.0 * (local_reg[1].value & REG03_LAMPTIM) *
|
|
1024.0) + 0.5);
|
|
/* 32000 = system clock, 24 = clocks per pixel */
|
|
rate = (exposure_time + 65536) / 65536;
|
|
if (rate > 4)
|
|
{
|
|
rate = 8;
|
|
tgtime = 3;
|
|
}
|
|
else if (rate > 2)
|
|
{
|
|
rate = 4;
|
|
tgtime = 2;
|
|
}
|
|
else if (rate > 1)
|
|
{
|
|
rate = 2;
|
|
tgtime = 1;
|
|
}
|
|
else
|
|
{
|
|
rate = 1;
|
|
tgtime = 0;
|
|
}
|
|
|
|
local_reg[5].value |= tgtime << 6;
|
|
exposure_time /= rate;
|
|
|
|
if (exposure_time > 65535)
|
|
exposure_time = 65535;
|
|
|
|
local_reg[3].value = exposure_time / 256; /* highbyte */
|
|
local_reg[4].value = exposure_time & 255; /* lowbyte */
|
|
|
|
status = gl646_bulk_write_register (dev, local_reg, sizeof (local_reg));
|
|
if (status != SANE_STATUS_GOOD)
|
|
DBG (DBG_error,
|
|
"gl646_set_powersaving: Failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
|
|
DBG (DBG_proc, "gl646_set_powersaving: completed\n");
|
|
return status;
|
|
}
|
|
|
|
/* Send the low-level scan command */
|
|
/* todo : is this that useful ? */
|
|
static SANE_Status
|
|
gl646_begin_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
|
|
SANE_Bool start_motor)
|
|
{
|
|
SANE_Status status;
|
|
Genesys_Register_Set local_reg[3];
|
|
|
|
DBG (DBG_proc, "gl646_begin_scan\n");
|
|
|
|
local_reg[0].address = 0x03;
|
|
local_reg[0].value = sanei_genesys_read_reg_from_set (reg, 0x03);
|
|
|
|
local_reg[1].address = 0x01;
|
|
local_reg[1].value = sanei_genesys_read_reg_from_set (reg, 0x01) | REG01_SCAN; /* set scan bit */
|
|
|
|
local_reg[2].address = 0x0f;
|
|
if (start_motor)
|
|
local_reg[2].value = 0x01;
|
|
else
|
|
local_reg[2].value = 0x00; /* do not start motor yet */
|
|
|
|
status = gl646_bulk_write_register (dev, local_reg, sizeof (local_reg));
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_begin_scan: Failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_begin_scan: completed\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/* Send the stop scan command */
|
|
static SANE_Status
|
|
gl646_end_scan (Genesys_Device * dev, Genesys_Register_Set * reg,
|
|
SANE_Bool check_stop)
|
|
{
|
|
SANE_Status status;
|
|
int i = 0;
|
|
u_int8_t val;
|
|
|
|
DBG (DBG_proc, "gl646_end_scan (check_stop = %d)\n", check_stop);
|
|
|
|
status = sanei_genesys_write_register (dev, 0x01, sanei_genesys_read_reg_from_set (reg, 0x01) & ~REG01_SCAN); /* disable scan */
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_end_scan: Failed to write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
if (check_stop)
|
|
{
|
|
for (i = 0; i < 300; i++) /* do not wait longer than wait 30 seconds */
|
|
{
|
|
status = sanei_genesys_get_status (dev, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_end_scan: Failed to read register: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
if ((!(val & REG41_MOTMFLG)) && (val & REG41_FEEDFSH))
|
|
{
|
|
DBG (DBG_proc, "gl646_end_scan: scanfeed finished\n");
|
|
break; /* leave while loop */
|
|
}
|
|
|
|
usleep (10000UL); /* sleep 100 ms */
|
|
}
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_end_scan: completed (i=%u)\n", i);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Moves the slider to the home (top) postion slowly */
|
|
static SANE_Status
|
|
gl646_slow_back_home (Genesys_Device * dev, SANE_Bool wait_until_home)
|
|
{
|
|
Genesys_Register_Set local_reg[GENESYS_GL646_MAX_REGS + 1];
|
|
SANE_Status status;
|
|
u_int8_t val = 0;
|
|
u_int8_t prepare_steps;
|
|
u_int32_t steps;
|
|
u_int16_t slope_table0[256];
|
|
u_int16_t exposure_time;
|
|
int i, dpi;
|
|
|
|
DBG (DBG_proc, "gl646_slow_back_home (wait_until_home = %d)\n",
|
|
wait_until_home);
|
|
|
|
memset (local_reg, 0, sizeof (local_reg));
|
|
status = sanei_genesys_get_status (dev, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: Failed to read home sensor: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
dev->scanhead_position_in_steps = 0;
|
|
|
|
if (val & REG41_HOMESNR) /* is sensor at home? */
|
|
{
|
|
DBG (DBG_info, "gl646_slow_back_home: already at home, completed\n");
|
|
dev->scanhead_position_in_steps = 0;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* stop motor if needed */
|
|
if (val & REG41_MOTMFLG)
|
|
{
|
|
status = sanei_genesys_start_motor (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: failed to start motor: %s\n",
|
|
sane_strstatus (status));
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
usleep (200000UL);
|
|
}
|
|
|
|
memcpy (local_reg, dev->reg, (GENESYS_GL646_MAX_REGS + 1) * 2);
|
|
|
|
prepare_steps = 4;
|
|
exposure_time = 6 * MOTOR_SPEED_MAX;
|
|
steps = 14700 - 2 * prepare_steps;
|
|
if (dev->model->motor_type == MOTOR_HP2300)
|
|
{
|
|
steps = 65535; /* enough to get back home ... */
|
|
dpi = dev->motor.base_ydpi / 4;
|
|
local_reg[reg_0x04].value &= ~REG04_FILTER;
|
|
exposure_time = sanei_genesys_exposure_time (dev, local_reg, dpi);
|
|
}
|
|
else
|
|
dpi = dev->motor.base_ydpi;
|
|
|
|
local_reg[reg_0x01].value =
|
|
local_reg[reg_0x01].value & ~REG01_FASTMOD & ~REG01_SCAN;
|
|
local_reg[reg_0x02].value = (local_reg[reg_0x02].value & ~REG02_FASTFED & ~REG02_STEPSEL) | REG02_MTRPWR | REG02_MTRREV; /* Motor on, direction = reverse */
|
|
if (dev->model->motor_type == MOTOR_HP2300)
|
|
local_reg[reg_0x02].value =
|
|
(local_reg[reg_0x02].value & ~REG02_STEPSEL) | REG02_HALFSTEP;
|
|
|
|
local_reg[reg_0x21].value = prepare_steps;
|
|
local_reg[reg_0x24].value = prepare_steps;
|
|
|
|
local_reg[reg_0x38].value = HIBYTE (exposure_time);
|
|
local_reg[reg_0x39].value = LOBYTE (exposure_time);
|
|
|
|
local_reg[reg_0x3d].value = LOBYTE (HIWORD (steps));
|
|
local_reg[reg_0x3e].value = HIBYTE (LOWORD (steps));
|
|
local_reg[reg_0x3f].value = LOBYTE (LOWORD (steps));
|
|
|
|
local_reg[reg_0x6c].value = 0x00; /* one time period (only one speed) */
|
|
if (dev->model->motor_type != MOTOR_HP2300)
|
|
{
|
|
local_reg[reg_0x66].value = local_reg[reg_0x66].value | REG66_LOW_CURRENT; /* gpio7-12: reset GPIO11 (low current) */
|
|
local_reg[reg_0x6d].value = 0x54;
|
|
}
|
|
|
|
if (dev->model->motor_type != MOTOR_UMAX)
|
|
{
|
|
sanei_genesys_create_slope_table (dev,
|
|
slope_table0,
|
|
local_reg[reg_0x21].value,
|
|
local_reg[reg_0x02].
|
|
value & REG02_STEPSEL, exposure_time,
|
|
0, dpi);
|
|
}
|
|
else
|
|
{
|
|
sanei_genesys_create_slope_table (dev, slope_table0, prepare_steps, 0,
|
|
exposure_time, 0,
|
|
dev->motor.
|
|
base_ydpi /*MOTOR_GEAR */ );
|
|
}
|
|
|
|
/* when scanhead is moving then wait until scanhead stops or timeout */
|
|
DBG (DBG_info, "gl646_slow_back_home: ensuring that motor is off\n");
|
|
for (i = 400; i > 0; i--) /* do not wait longer than 40 seconds, count down to get i = 0 when busy */
|
|
{
|
|
status = sanei_genesys_get_status (dev, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: Failed to read home sensor & motor status: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
if (((val & (REG41_MOTMFLG | REG41_HOMESNR)) == REG41_HOMESNR)) /* at home and motor is off */
|
|
{
|
|
DBG (DBG_info,
|
|
"gl646_slow_back_home: already at home and nor moving\n");
|
|
dev->scanhead_position_in_steps = 0;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
if (!(val & REG41_MOTMFLG)) /* motor is off (todo: was this really wrong?) */
|
|
{
|
|
DBG (DBG_info,
|
|
"gl646_slow_back_home: motor is off but scanhead is not home\n");
|
|
break; /* motor is off and scanhead is not at home: continue */
|
|
}
|
|
|
|
usleep (100 * 1000); /* sleep 100 ms (todo: fixed to really sleep 100 ms) */
|
|
}
|
|
|
|
if (!i) /* the loop counted down to 0, scanner still is busy */
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: motor is still on: device busy\n");
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 0, slope_table0, local_reg[reg_0x21].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: Failed to send slope table 0: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_bulk_write_register (dev, local_reg, GENESYS_GL646_MAX_REGS * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: Failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sanei_genesys_start_motor (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_slow_back_home: Failed to start motor: %s\n",
|
|
sane_strstatus (status));
|
|
sanei_genesys_stop_motor (dev);
|
|
/* send original registers */
|
|
gl646_bulk_write_register (dev, dev->reg, GENESYS_GL646_MAX_REGS * 2);
|
|
return status;
|
|
}
|
|
|
|
if (wait_until_home)
|
|
{
|
|
int loop = 0;
|
|
|
|
while (loop < 3) /* do not wait longer then 30 seconds */
|
|
{
|
|
status = sanei_genesys_get_status (dev, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: Failed to read home sensor: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
if (val & 0x08) /* home sensor */
|
|
{
|
|
DBG (DBG_info, "gl646_slow_back_home: reached home position\n");
|
|
DBG (DBG_proc, "gl646_slow_back_home: finished\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
usleep (100000); /* sleep 100 ms */
|
|
}
|
|
|
|
/* when we come here then the scanner needed too much time for this, so we better stop the motor */
|
|
sanei_genesys_stop_motor (dev);
|
|
DBG (DBG_error,
|
|
"gl646_slow_back_home: timeout while waiting for scanhead to go home\n");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
DBG (DBG_info, "gl646_slow_back_home: scanhead is still moving\n");
|
|
DBG (DBG_proc, "gl646_slow_back_home: finished\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* Moves the slider to the home (top) postion */
|
|
/* relies on the settings given for scan,
|
|
but disables scan, puts motor in reverse and
|
|
steps to go before area to 65535 so head will
|
|
go home */
|
|
static SANE_Status
|
|
gl646_park_head (Genesys_Device * dev, Genesys_Register_Set * reg,
|
|
SANE_Bool wait_until_home)
|
|
{
|
|
Genesys_Register_Set local_reg[9];
|
|
SANE_Status status;
|
|
u_int8_t val = 0;
|
|
int loop = 0;
|
|
int i;
|
|
int exposure_time;
|
|
|
|
DBG (DBG_proc, "gl646_park_head (wait_until_home = %d)\n", wait_until_home);
|
|
memset (local_reg, 0, sizeof (local_reg));
|
|
|
|
/* read status */
|
|
status = sanei_genesys_get_status (dev, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_park_head: failed to read home sensor: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* no need to park if allready at home */
|
|
if (val & REG41_HOMESNR)
|
|
{
|
|
dev->scanhead_position_in_steps = 0;
|
|
return status;
|
|
}
|
|
|
|
/* stop motor if needed */
|
|
if (val & REG41_MOTMFLG)
|
|
{
|
|
status = sanei_genesys_stop_motor (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_search_par_head: failed to stop motor: %s\n",
|
|
sane_strstatus (status));
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
usleep (200 * 1000);
|
|
}
|
|
|
|
/* toggle values to get backward move */
|
|
i = 0;
|
|
/* disable scan */
|
|
local_reg[i].address = 0x01;
|
|
local_reg[i++].value = reg[reg_0x01].value & ~REG01_SCAN;
|
|
|
|
/* motor reverse */
|
|
local_reg[i].address = 0x02;
|
|
local_reg[i].value = reg[reg_0x02].value | REG02_MTRREV;
|
|
|
|
switch (dev->model->motor_type)
|
|
{
|
|
case MOTOR_HP2300:
|
|
exposure_time = 2000;
|
|
local_reg[i++].value &= ~REG02_FASTFED;
|
|
break;
|
|
case MOTOR_HP2400:
|
|
exposure_time = 675;
|
|
local_reg[i++].value &= ~REG02_FASTFED;
|
|
break;
|
|
case MOTOR_5345:
|
|
exposure_time = 3600;
|
|
local_reg[i++].value |= REG02_FASTFED;
|
|
break;
|
|
default:
|
|
exposure_time = 3600;
|
|
local_reg[i++].value &= ~REG02_FASTFED;
|
|
break;
|
|
}
|
|
|
|
/* enough steps (65535) to get home */
|
|
local_reg[i].address = 0x3d;
|
|
local_reg[i++].value = 0x00;
|
|
local_reg[i].address = 0x3e;
|
|
local_reg[i++].value = HIBYTE (65535);
|
|
local_reg[i].address = 0x3f;
|
|
local_reg[i++].value = LOBYTE (65535);
|
|
|
|
/* set line period */
|
|
local_reg[i].address = 0x38;
|
|
local_reg[i++].value = HIBYTE (exposure_time);
|
|
local_reg[i].address = 0x39;
|
|
local_reg[i++].value = LOBYTE (exposure_time);
|
|
|
|
/* writes register */
|
|
status = gl646_bulk_write_register (dev, local_reg, i * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_park_head: failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* sends slope table 1 (move before scan area) */
|
|
status = gl646_send_slope_table (dev, 1, dev->slope_table1,
|
|
reg[reg_0x6b].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_park_head: failed to send slope table 1: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* start motor */
|
|
status = sanei_genesys_start_motor (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_park_head: failed to start motor: %s\n",
|
|
sane_strstatus (status));
|
|
sanei_genesys_stop_motor (dev);
|
|
/* restore original registers */
|
|
gl646_bulk_write_register (dev, reg, GENESYS_GL646_MAX_REGS * 2);
|
|
return status;
|
|
}
|
|
|
|
/* wait for head to park if needed */
|
|
if (wait_until_home)
|
|
{
|
|
/* no more than 300 loops of 100 ms each -> 30 second time out */
|
|
while (loop < 300)
|
|
{
|
|
status = sanei_genesys_get_status (dev, &val);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_park_head: failed to read home sensor: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* test home sensor */
|
|
if (val & REG41_HOMESNR)
|
|
{
|
|
DBG (DBG_info, "gl646_park_head: reached home position\n");
|
|
DBG (DBG_proc, "gl646_park_head: finished\n");
|
|
dev->scanhead_position_in_steps = 0;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
/* hack around a bug ? */
|
|
if (!(val & REG41_MOTMFLG))
|
|
{
|
|
DBG (DBG_info, "gl646_park_head: restarting motor\n");
|
|
status = sanei_genesys_start_motor (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_park_head: failed to restart motor: %s\n",
|
|
sane_strstatus (status));
|
|
}
|
|
}
|
|
usleep (100000);
|
|
}
|
|
}
|
|
|
|
/* when we come here then the scanner needed too much time for this,
|
|
so we better stop the motor */
|
|
sanei_genesys_start_motor (dev);
|
|
DBG (DBG_error,
|
|
"gl646_park_head: timeout while waiting for scanhead to go home\n");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
|
|
/* Automatically set top-left edge of the scan area by scanning a 200x200 pixels
|
|
area at 600 dpi from very top of scanner */
|
|
static SANE_Status
|
|
gl646_search_start_position (Genesys_Device * dev)
|
|
{
|
|
int size;
|
|
SANE_Status status;
|
|
u_int8_t *data;
|
|
u_int16_t slope_table0[256];
|
|
Genesys_Register_Set local_reg[GENESYS_GL646_MAX_REGS + 1];
|
|
int i, steps;
|
|
int exposure_time, half_ccd = 0;
|
|
struct timeval tv;
|
|
|
|
int pixels = 600, depth, words;
|
|
int dpi = 300;
|
|
int start_pixel, end_pixel;
|
|
|
|
DBG (DBG_proc, "gl646_search_start_position\n");
|
|
memset (local_reg, 0, sizeof (local_reg));
|
|
memcpy (local_reg, dev->reg, (GENESYS_GL646_MAX_REGS + 1) * 2);
|
|
|
|
gettimeofday (&tv, NULL);
|
|
/* is scanner warm enough ? */
|
|
/* we have 2 options there, either wait, or return SANE_STATUS_DEVICE_BUSY */
|
|
/* we'd rather wait ... */
|
|
if ((dev->model->flags & GENESYS_FLAG_MUST_WAIT)
|
|
&& (tv.tv_sec - dev->init_date < 60))
|
|
{
|
|
DBG (DBG_proc,
|
|
"gl646_search_start_position: waiting for scanner to be ready\n");
|
|
usleep (1000 * 1000 * (tv.tv_sec - dev->init_date));
|
|
}
|
|
|
|
/* sets for a 200 lines * 600 pixels */
|
|
/* normal scan with no shading */
|
|
local_reg[reg_0x01].value =
|
|
(local_reg[reg_0x01].
|
|
value & (REG01_CISSET | REG01_DRAMSEL | REG01_DOGENB)) | (REG01_SCAN |
|
|
REG01_DOGENB);
|
|
if (dev->model->ccd_type == CCD_HP2300
|
|
|| dev->model->ccd_type == CCD_HP2400)
|
|
{
|
|
local_reg[reg_0x02].value = 0x30;
|
|
half_ccd = 0;
|
|
}
|
|
else
|
|
{
|
|
local_reg[reg_0x02].value = 0xd0; /* auto-go-home disabled, disable moving when buffer full, auto-go-home after scan disabled, turn on MOTOR power and phase, one table motor moving, motor forward */
|
|
half_ccd = 1;
|
|
}
|
|
local_reg[reg_0x02].value =
|
|
(local_reg[reg_0x02].value | REG02_HALFSTEP) & ~REG02_AGOHOME;
|
|
|
|
/* we are doing a monochrome scan */
|
|
local_reg[reg_0x04].value = 0x57; /* color lineart, 16 bits data, frontend type 16 bits, scan color type R, frontend B */
|
|
gl646_setup_sensor (dev, local_reg, 1, half_ccd);
|
|
|
|
if (dev->model->ccd_type == CCD_HP2300
|
|
|| dev->model->ccd_type == CCD_HP2400)
|
|
{
|
|
local_reg[reg_0x04].value &= ~(REG04_BITSET | REG04_FILTER);
|
|
local_reg[reg_0x04].value |= 0x08; /* green filter */
|
|
local_reg[reg_0x05].value |= REG05_GMMENB;
|
|
depth = 8;
|
|
steps = 18;
|
|
}
|
|
else
|
|
{
|
|
local_reg[reg_0x05].value &= ~REG05_GMMENB;
|
|
depth = 16;
|
|
/*local_reg[reg_0x1f].value = 0x01; */
|
|
steps = 1;
|
|
}
|
|
|
|
/* no dummy lines */
|
|
local_reg[reg_0x1e].value &= 0xf0;
|
|
if (dev->model->ccd_type == CCD_HP2300
|
|
|| dev->model->ccd_type == CCD_HP2400)
|
|
local_reg[reg_0x1e].value = 0x40;
|
|
|
|
/* step move */
|
|
local_reg[reg_0x3d].value = LOBYTE (HIWORD (steps));
|
|
local_reg[reg_0x3e].value = HIBYTE (LOWORD (steps));
|
|
local_reg[reg_0x3f].value = LOBYTE (LOWORD (steps));
|
|
|
|
/* reset GPO */
|
|
local_reg[reg_0x66].value = dev->gpo.value[0];
|
|
local_reg[reg_0x67].value = dev->gpo.value[1];
|
|
local_reg[reg_0x68].value = dev->gpo.enable[0];
|
|
local_reg[reg_0x69].value = dev->gpo.enable[1];
|
|
|
|
exposure_time = sanei_genesys_exposure_time (dev, local_reg, dpi);
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
gl646_setup_steps (dev, local_reg, dpi);
|
|
else
|
|
{
|
|
local_reg[reg_0x21].value = 3;
|
|
local_reg[reg_0x22].value = 16;
|
|
local_reg[reg_0x23].value = 16;
|
|
local_reg[reg_0x24].value = 3;
|
|
}
|
|
|
|
/* number of scan lines */
|
|
local_reg[reg_0x25].value = LOBYTE (HIWORD (dev->model->search_lines));
|
|
local_reg[reg_0x26].value = HIBYTE (LOWORD (dev->model->search_lines));
|
|
local_reg[reg_0x27].value = LOBYTE (LOWORD (dev->model->search_lines));
|
|
|
|
/* DPISET is at CCD max, word count gives the ratio, and the real dpi */
|
|
local_reg[reg_0x2c].value = HIBYTE (dev->sensor.optical_res);
|
|
local_reg[reg_0x2d].value = LOBYTE (dev->sensor.optical_res);
|
|
|
|
/* start pixel */
|
|
start_pixel = dev->sensor.dummy_pixel;
|
|
if (dev->model->ccd_type == CCD_HP2300)
|
|
start_pixel += 64;
|
|
local_reg[reg_0x30].value = HIBYTE (start_pixel);
|
|
local_reg[reg_0x31].value = LOBYTE (start_pixel);
|
|
|
|
|
|
/* end CCD pixel */
|
|
end_pixel = start_pixel + pixels;
|
|
local_reg[reg_0x32].value = HIBYTE (end_pixel);
|
|
local_reg[reg_0x33].value = LOBYTE (end_pixel);
|
|
|
|
words = (pixels * depth) / 8;
|
|
local_reg[reg_0x35].value = LOBYTE (HIWORD (words));
|
|
local_reg[reg_0x36].value = HIBYTE (LOWORD (words)); /* */
|
|
local_reg[reg_0x37].value = LOBYTE (LOWORD (words)); /* maximum word size per line=1200 */
|
|
|
|
local_reg[reg_0x38].value = HIBYTE (exposure_time); /* half default period of 11000 */
|
|
local_reg[reg_0x39].value = LOBYTE (exposure_time); /* CCD line period set to 5500 */
|
|
|
|
/* Z1MOD = Z2MOD = 0 */
|
|
local_reg[reg_0x60].value = LOBYTE (0);
|
|
local_reg[reg_0x61].value = HIBYTE (0);
|
|
local_reg[reg_0x62].value = LOBYTE (0);
|
|
local_reg[reg_0x63].value = HIBYTE (0);
|
|
|
|
if (dev->model->motor_type != MOTOR_HP2300)
|
|
local_reg[reg_0x65].value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
|
|
else
|
|
{
|
|
local_reg[reg_0x65].value = 0x3f;
|
|
local_reg[reg_0x6a].value = 0x7f; /* table two fast moving step type, PWM duty for table two */
|
|
local_reg[reg_0x6d].value = 0x7f; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
|
|
}
|
|
|
|
/* send to scanner */
|
|
status =
|
|
gl646_bulk_write_register (dev, local_reg, GENESYS_GL646_MAX_REGS * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* create local slope tables */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
sanei_genesys_create_slope_table (dev,
|
|
slope_table0,
|
|
local_reg[reg_0x21].value,
|
|
local_reg[reg_0x02].
|
|
value & REG02_STEPSEL, exposure_time, 0,
|
|
dpi);
|
|
else
|
|
sanei_genesys_create_slope_table (dev,
|
|
slope_table0,
|
|
local_reg[reg_0x21].value,
|
|
local_reg[reg_0x02].
|
|
value & REG02_STEPSEL, exposure_time, 0,
|
|
dev->motor.base_ydpi);
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 0, slope_table0, local_reg[reg_0x21].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to send slope table 0: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
size = words * (dev->model->search_lines + 1);
|
|
|
|
data = malloc (size);
|
|
if (!data)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to allocate memory\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
status = gl646_set_fe (dev, AFE_INIT);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (data);
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to set frontend: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = gl646_begin_scan (dev, local_reg, SANE_TRUE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (data);
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to begin scan: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* waits for valid data */
|
|
do
|
|
sanei_genesys_test_buffer_empty (dev, &steps);
|
|
while (steps);
|
|
|
|
/* now we're on target, we can read data */
|
|
status = sanei_genesys_read_data_from_scanner (dev, data, size);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (data);
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to read data: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* start position search is done on 8 bit data */
|
|
if (depth == 16)
|
|
{
|
|
if (DBG_LEVEL >= DBG_data)
|
|
sanei_genesys_write_pnm_file ("search_position16.pnm", data, depth, 1,
|
|
pixels, dev->model->search_lines);
|
|
size /= 2;
|
|
for (i = 0; i < size; i++)
|
|
data[i] = data[i * 2 + 1];
|
|
}
|
|
|
|
/* x resolution may be the double of y resolution */
|
|
/* after that we have a 300 dpi gray level picture */
|
|
if (dev->sensor.optical_res > 2 * dpi)
|
|
{
|
|
size /= 2;
|
|
for (i = 0; i < size; i++)
|
|
data[i] = (data[i * 2] + data[i * 2 + 1]) / 2;
|
|
pixels /= 2;
|
|
}
|
|
|
|
if (DBG_LEVEL >= DBG_data)
|
|
sanei_genesys_write_pnm_file ("search_position.pnm", data, 8, 1, pixels,
|
|
dev->model->search_lines);
|
|
|
|
status = gl646_end_scan (dev, local_reg, SANE_FALSE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (data);
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to end scan: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* update regs to copy ASIC internal state */
|
|
dev->reg[reg_0x01].value = local_reg[reg_0x01].value;
|
|
dev->reg[reg_0x02].value = local_reg[reg_0x02].value;
|
|
memcpy (dev->reg, local_reg, (GENESYS_GL646_MAX_REGS + 1) * 2);
|
|
|
|
status =
|
|
sanei_genesys_search_reference_point (dev, data, start_pixel, dpi, pixels,
|
|
dev->model->search_lines);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (data);
|
|
DBG (DBG_error,
|
|
"gl646_search_start_position: failed to set search reference point: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
free (data);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
* sets up register for coarse gain calibration
|
|
* todo: check it for scanners using it */
|
|
static SANE_Status
|
|
gl646_init_regs_for_coarse_calibration (Genesys_Device * dev)
|
|
{
|
|
SANE_Status status;
|
|
u_int32_t bytes_per_line;
|
|
u_int32_t words_per_line;
|
|
u_int32_t steps_sum;
|
|
u_int32_t z1, z2;
|
|
u_int16_t slope_table[256];
|
|
u_int16_t strpixel;
|
|
u_int16_t endpixel;
|
|
u_int8_t channels;
|
|
u_int8_t cksel;
|
|
|
|
DBG (DBG_proc, "gl646_init_regs_for_coarse_calibration\n");
|
|
|
|
/* ST12:
|
|
0x01 0x00 0x02 0x71 0x03 0x1f 0x04 0x57 0x05 0x10 0x06 0x18 0x08 0x02 0x09 0x00
|
|
0x0a 0x06 0x0b 0x04
|
|
0x10 0x00 0x11 0x00 0x12 0x00 0x13 0x00 0x14 0x00 0x15 0x00 0x16 0x2b 0x17 0x08 0x18 0x20 0x19 0x2a
|
|
0x1c 0xc0 0x1d 0x03 */
|
|
|
|
/* shading off, compression off */
|
|
dev->calib_reg[reg_0x01].value = REG01_SCAN;
|
|
/* disable forward/backward+autogohome+fastfed+motor */
|
|
dev->calib_reg[reg_0x02].value =
|
|
(dev->calib_reg[reg_0x02].
|
|
value & ~REG02_MTRPWR & ~REG02_AGOHOME & ~REG02_FASTFED & ~REG02_STEPSEL)
|
|
| REG02_ACDCDIS;
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
dev->calib_reg[reg_0x02].value |= REG02_QUATERSTEP;
|
|
|
|
/* disable lineart, enable 16bit */
|
|
dev->calib_reg[reg_0x04].value =
|
|
(dev->calib_reg[reg_0x04].value & ~REG04_LINEART) | REG04_BITSET;
|
|
dev->calib_reg[reg_0x05].value = (dev->calib_reg[reg_0x05].value & ~REG05_GMMENB); /* disable gamma */
|
|
|
|
/* ST12:
|
|
0x21 0x20 0x22 0x10 0x23 0x10 0x24 0x20 0x25 0x00 0x26 0x00 0x27 0x64
|
|
0x32 0x02 0x33 0x66 0x35 0x00 0x36 0x04 0x37 0xb0 0x38 0x27 0x39 0x10
|
|
0x52 0x0f 0x53 0x13 0x54 0x17 0x55 0x03 0x56 0x07 0x57 0x0b 0x58 0x83
|
|
0x65 0x3f 0x6b 0xff */
|
|
|
|
dev->calib_reg[reg_0x21].value = 0x20 /*0x01 */ ;
|
|
dev->calib_reg[reg_0x22].value = 0x10 /*0x00 */ ;
|
|
dev->calib_reg[reg_0x23].value = 0x10 /*0x01 */ ;
|
|
dev->calib_reg[reg_0x24].value = 0x20 /*0x00 */ ;
|
|
dev->calib_reg[reg_0x25].value = 0x00;
|
|
dev->calib_reg[reg_0x26].value = 0x00;
|
|
dev->calib_reg[reg_0x27].value = 0x64 /*0x01 */ ;
|
|
dev->calib_reg[reg_0x3d].value = 0x00;
|
|
dev->calib_reg[reg_0x3e].value = 0x00;
|
|
dev->calib_reg[reg_0x3f].value = 0x00;
|
|
/* dev->calib_reg[reg_0x6b].value = 0xff; done in init *//*0x01 */
|
|
|
|
cksel = (dev->calib_reg[reg_0x18].value & REG18_CKSEL) + 1; /* clock speed = 1..4 clocks */
|
|
|
|
/* start position */
|
|
strpixel = dev->sensor.dummy_pixel;
|
|
dev->calib_reg[reg_0x30].value = HIBYTE (strpixel);
|
|
dev->calib_reg[reg_0x31].value = LOBYTE (strpixel);
|
|
|
|
/* set right position todo: optical resolution, not real x resolution? */
|
|
endpixel = strpixel + (dev->sensor.optical_res / cksel);
|
|
|
|
dev->calib_reg[reg_0x32].value = HIBYTE (endpixel);
|
|
dev->calib_reg[reg_0x33].value = LOBYTE (endpixel);
|
|
DBG (DBG_info,
|
|
"gl646_init_register_for_coarse_calibration: left pos: %d CCD pixels, right pos: %d CCD pixels\n",
|
|
strpixel, endpixel);
|
|
|
|
/* set dummy pixels */
|
|
/* todo: dummy pixels + tgw + 2* tgs according spec? */
|
|
dev->calib_reg[reg_0x34].value = dev->sensor.dummy_pixel;
|
|
DBG (DBG_info,
|
|
"gl646_init_register_for_coarse_calibration: dummy pixels: %d CCD pixels\n",
|
|
dev->sensor.dummy_pixel);
|
|
|
|
DBG (DBG_info,
|
|
"gl646_init_register_for_coarse_calibration: optical sensor res: %d dpi, actual res: %d\n",
|
|
dev->sensor.optical_res / cksel, dev->settings.xres);
|
|
|
|
/* set line size */
|
|
if (dev->settings.scan_mode == 4) /* single pass color */
|
|
channels = 3;
|
|
else
|
|
channels = 1;
|
|
|
|
bytes_per_line = channels * 2 * (endpixel - strpixel);
|
|
|
|
words_per_line = (bytes_per_line + 1) / 2 + 1;
|
|
|
|
dev->calib_reg[reg_0x35].value = LOBYTE (HIWORD (words_per_line));
|
|
dev->calib_reg[reg_0x36].value = HIBYTE (LOWORD (words_per_line));
|
|
dev->calib_reg[reg_0x37].value = LOBYTE (LOWORD (words_per_line));
|
|
|
|
DBG (DBG_info,
|
|
"gl646_init_register_for_coarse_calibration: bytes_per_line=%d, words_per_line=%d\n",
|
|
bytes_per_line, words_per_line);
|
|
|
|
/* todo: only 2 steps? */
|
|
|
|
steps_sum =
|
|
sanei_genesys_create_slope_table (dev, slope_table,
|
|
dev->calib_reg[reg_0x21].value,
|
|
dev->calib_reg[reg_0x02].
|
|
value & REG02_STEPSEL,
|
|
dev->settings.exposure_time, 0,
|
|
dev->motor.base_ydpi /*MOTOR_GEAR */ );
|
|
|
|
/* todo: z1 = z2 = 0? */
|
|
sanei_genesys_calculate_zmode (dev,
|
|
dev->settings.exposure_time,
|
|
steps_sum,
|
|
slope_table[dev->calib_reg[reg_0x21].value -
|
|
1],
|
|
dev->calib_reg[reg_0x3d].value * 65536 +
|
|
dev->calib_reg[reg_0x3e].value * 256 +
|
|
dev->calib_reg[reg_0x3f].value,
|
|
dev->calib_reg[reg_0x02].
|
|
value & REG02_FASTFED,
|
|
dev->calib_reg[reg_0x1f].value,
|
|
dev->calib_reg[reg_0x22].value,
|
|
(dev->calib_reg[reg_0x6c].
|
|
value & REG6C_TGTIME) >> 6, &z1, &z2);
|
|
DBG (DBG_info, "gl646_init_register_for_coarse_calibration: z1 = %d\n",
|
|
z1);
|
|
DBG (DBG_info, "gl646_init_register_for_coarse_calibration: z2 = %d\n",
|
|
z2);
|
|
dev->calib_reg[reg_0x60].value = HIBYTE (z1);
|
|
dev->calib_reg[reg_0x61].value = LOBYTE (z1);
|
|
dev->calib_reg[reg_0x62].value = HIBYTE (z2);
|
|
dev->calib_reg[reg_0x63].value = LOBYTE (z2);
|
|
dev->calib_reg[reg_0x6c].value = (dev->calib_reg[reg_0x6c].value & REG6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16) & 0x07); /* todo: double check */
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 0, slope_table,
|
|
dev->calib_reg[reg_0x21].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_register_for_coarse_calibration: Failed to send slope table: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_bulk_write_register (dev, dev->calib_reg,
|
|
GENESYS_GL646_MAX_REGS * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_register_for_coarse_calibration: Failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_init_register_for_coarse_calibration: completed\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
/* init registers for shading calibration */
|
|
static SANE_Status
|
|
gl646_init_regs_for_shading (Genesys_Device * dev)
|
|
{
|
|
SANE_Status status;
|
|
u_int32_t bytes_per_line;
|
|
u_int32_t num_pixels;
|
|
u_int32_t steps_sum;
|
|
int move = 0;
|
|
int exposure_time;
|
|
u_int32_t z1, z2;
|
|
u_int16_t slope_table[256];
|
|
u_int16_t steps;
|
|
u_int8_t step_parts;
|
|
u_int8_t channels;
|
|
u_int8_t dummy_lines = 3;
|
|
int dpiset;
|
|
SANE_Bool half_ccd;
|
|
int workaround;
|
|
|
|
DBG (DBG_proc, "gl646_init_register_for_shading: lines = %d\n",
|
|
dev->model->shading_lines);
|
|
|
|
if (dev->settings.scan_mode == 4) /* single pass color */
|
|
channels = 3;
|
|
else
|
|
channels = 1;
|
|
|
|
/* monochrome / color scan */
|
|
dev->calib_reg[reg_0x04].value &= ~REG04_FILTER;
|
|
if (channels == 1)
|
|
dev->calib_reg[reg_0x04].value |= 0x08;
|
|
|
|
/* disable forward/backward+motor, stepsel = 1 */
|
|
dev->calib_reg[reg_0x02].value =
|
|
(dev->calib_reg[reg_0x02].
|
|
value & ~REG02_MTRPWR & ~REG02_STEPSEL & ~REG02_AGOHOME) | REG02_ACDCDIS
|
|
| 0x01;
|
|
|
|
/* dpi=600 for scan at 600 dpi and below, and dpi=1200 when above */
|
|
dpiset = dev->sensor.optical_res;
|
|
|
|
if (dev->settings.xres <= dev->sensor.optical_res / 2)
|
|
{
|
|
num_pixels = dev->sensor.sensor_pixels / 2;
|
|
half_ccd = 1;
|
|
}
|
|
else
|
|
{
|
|
num_pixels = dev->sensor.sensor_pixels;
|
|
half_ccd = 0;
|
|
}
|
|
exposure_time =
|
|
sanei_genesys_exposure_time (dev, dev->calib_reg, dev->settings.yres);
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_shading: half_ccd = %d, yres = %d\n",
|
|
half_ccd, dev->settings.yres);
|
|
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
dev->calib_reg[reg_0x02].value =
|
|
REG02_ACDCDIS | REG02_MTRPWR | REG02_QUATERSTEP;
|
|
dev->calib_reg[reg_0x03].value |= REG03_AVEENB;
|
|
dev->calib_reg[reg_0x04].value |= REG04_BITSET;
|
|
dev->calib_reg[reg_0x05].value &= ~REG05_GMMENB;
|
|
}
|
|
|
|
/* full, half or quarter step, 0x11 is reserved! (--> 1 << 1 = 0x02) */
|
|
step_parts = (1 << (dev->calib_reg[reg_0x02].value & REG02_STEPSEL));
|
|
steps = step_parts * 0x40; /* base = 64 lines --> 0x80 */
|
|
if (steps > 255)
|
|
steps = 255;
|
|
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
gl646_setup_steps (dev, dev->calib_reg, dpiset);
|
|
else if (dev->model->motor_type == MOTOR_HP2300)
|
|
{
|
|
dev->calib_reg[reg_0x21].value = 2;
|
|
dev->calib_reg[reg_0x22].value = 16;
|
|
}
|
|
else
|
|
{
|
|
dev->calib_reg[reg_0x21].value = (u_int8_t) steps;
|
|
steps = 2 * step_parts;
|
|
if (steps > 255)
|
|
steps = 255;
|
|
|
|
dev->calib_reg[reg_0x22].value = (u_int8_t) steps;
|
|
}
|
|
dev->calib_reg[reg_0x23].value = dev->calib_reg[reg_0x22].value;
|
|
dev->calib_reg[reg_0x24].value = dev->calib_reg[reg_0x21].value;
|
|
|
|
|
|
steps = dev->model->y_offset * step_parts;
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
steps = 0;
|
|
else if (dev->model->motor_type == MOTOR_HP2300)
|
|
steps = 1;
|
|
else
|
|
dev->calib_reg[reg_0x6b].value = 0x20 * step_parts;
|
|
|
|
bytes_per_line =
|
|
(channels * 2 * num_pixels * dpiset) / dev->sensor.optical_res;
|
|
|
|
dev->calib_reg[reg_0x25].value =
|
|
LOBYTE (HIWORD (dev->model->shading_lines));
|
|
dev->calib_reg[reg_0x26].value =
|
|
HIBYTE (LOWORD (dev->model->shading_lines));
|
|
dev->calib_reg[reg_0x27].value =
|
|
LOBYTE (LOWORD (dev->model->shading_lines));
|
|
|
|
dev->calib_reg[reg_0x2c].value = HIBYTE (dpiset);
|
|
dev->calib_reg[reg_0x2d].value = LOBYTE (dpiset);
|
|
|
|
dev->calib_reg[reg_0x30].value = HIBYTE (dev->sensor.dummy_pixel);
|
|
dev->calib_reg[reg_0x31].value = LOBYTE (dev->sensor.dummy_pixel);
|
|
dev->calib_reg[reg_0x32].value =
|
|
HIBYTE (dev->sensor.dummy_pixel + num_pixels);
|
|
dev->calib_reg[reg_0x33].value =
|
|
LOBYTE (dev->sensor.dummy_pixel + num_pixels);
|
|
|
|
dev->calib_reg[reg_0x35].value = LOBYTE (HIWORD (bytes_per_line));
|
|
dev->calib_reg[reg_0x36].value = HIBYTE (LOWORD (bytes_per_line));
|
|
dev->calib_reg[reg_0x37].value = LOBYTE (LOWORD (bytes_per_line));
|
|
|
|
dev->calib_reg[reg_0x38].value = HIBYTE (exposure_time);
|
|
dev->calib_reg[reg_0x39].value = LOBYTE (exposure_time);
|
|
|
|
dev->calib_reg[reg_0x3d].value = LOBYTE (HIWORD (steps));
|
|
dev->calib_reg[reg_0x3e].value = HIBYTE (LOWORD (steps));
|
|
dev->calib_reg[reg_0x3f].value = LOBYTE (LOWORD (steps));
|
|
|
|
gl646_setup_sensor (dev, dev->calib_reg, 1, half_ccd);
|
|
|
|
z1 = 0;
|
|
z2 = 0;
|
|
if (dev->model->motor_type != MOTOR_5345)
|
|
{
|
|
dpiset /= (1 + half_ccd);
|
|
steps_sum = sanei_genesys_create_slope_table (dev,
|
|
slope_table,
|
|
dev->calib_reg[reg_0x21].
|
|
value,
|
|
dev->calib_reg[reg_0x02].
|
|
value & REG02_STEPSEL,
|
|
sanei_genesys_exposure_time
|
|
(dev, dev->reg, dpiset),
|
|
0, dpiset);
|
|
move = (dev->model->shading_lines * dev->motor.base_ydpi) / dpiset;
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_shading: computed move = %d, dpiset = %d\n",
|
|
move, dpiset);
|
|
/* todo: sort this out, there's on offset somewhere ... */
|
|
/* I really don't get what's going on here ... */
|
|
/* Or move computing is wrong when doing reg init for final scan */
|
|
if (dev->settings.yres == 300)
|
|
move = 115;
|
|
else if (dev->settings.yres == 150)
|
|
move = 0;
|
|
else if (dev->settings.yres == 75)
|
|
move = 60;
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_shading: overrided move = %d, yres = %d\n",
|
|
move, dev->settings.yres);
|
|
}
|
|
else
|
|
{
|
|
dev->calib_reg[reg_0x21].value = 1;
|
|
dev->calib_reg[reg_0x6b].value = 255;
|
|
|
|
/* seems to be 2 if dpi <= 150, but is it useful ? */
|
|
if (dev->settings.yres <= 150)
|
|
dev->calib_reg[reg_0x65].value = 2;
|
|
else
|
|
dev->calib_reg[reg_0x65].value = 0;
|
|
|
|
/* specific slope-table for shading */
|
|
/* it appears win200 and winMe aren't using the same speeds */
|
|
/* consumer / pro settings ? */
|
|
/* this is currently real magic to me */
|
|
/* todo: find out the real rule */
|
|
/* quarter steps for every resolution */
|
|
if (dev->settings.yres <= 150) /* 11000 exposure time */
|
|
{
|
|
slope_table[0] = 3471;
|
|
dummy_lines = 2;
|
|
workaround = -390;
|
|
}
|
|
else if (dev->settings.yres <= 300) /* 5500 exposure time */
|
|
{
|
|
slope_table[0] = 4400;
|
|
dummy_lines = 7;
|
|
workaround = -260;
|
|
}
|
|
else if (dev->settings.yres <= 600) /* 11000 exposure time */
|
|
{
|
|
slope_table[0] = 4400;
|
|
if (channels > 1)
|
|
dummy_lines = 3;
|
|
else
|
|
dummy_lines = 1;
|
|
workaround = -600;
|
|
}
|
|
else if (dev->settings.yres <= 800) /* 11000 exposure time */
|
|
{
|
|
slope_table[0] = 9258;
|
|
dummy_lines = 7;
|
|
workaround = -600;
|
|
}
|
|
else
|
|
{
|
|
slope_table[0] = 8378;
|
|
dummy_lines = 7;
|
|
workaround = -600;
|
|
}
|
|
|
|
/* computes motor steps done during shading calibration
|
|
dpi = slope_table[0] * dev->motor.base_ydpi * step_parts / (exposure_time * (1+dummy_lines));
|
|
move = (lines / dpi) * dev->motor.optical_ydpi */
|
|
move =
|
|
(dev->model->shading_lines * exposure_time * (1 + dummy_lines) * 2) /
|
|
(step_parts * slope_table[0]);
|
|
|
|
/* todo: find a way to remove this workaround */
|
|
move -= (dev->model->shading_lines * workaround) / 32;
|
|
|
|
/* rest of table */
|
|
for (steps = 1; steps < 255; steps++)
|
|
slope_table[steps] = slope_table[0];
|
|
|
|
/* set all available bits */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
dev->calib_reg[reg_0x66].value = 0x30;
|
|
dev->calib_reg[reg_0x67].value = dev->gpo.enable[1];
|
|
}
|
|
}
|
|
|
|
DBG (DBG_info, "gl646_init_regs_for_shading: move = %d\n", move);
|
|
dev->calib_reg[reg_0x60].value = HIBYTE (z1);
|
|
dev->calib_reg[reg_0x61].value = LOBYTE (z1);
|
|
dev->calib_reg[reg_0x62].value = HIBYTE (z2);
|
|
dev->calib_reg[reg_0x63].value = LOBYTE (z2);
|
|
|
|
/* linesel = 3 */
|
|
dev->calib_reg[reg_0x1e].value =
|
|
(dev->calib_reg[reg_0x1e].value & ~REG1E_LINESEL) | dummy_lines;
|
|
|
|
/* dark calibration move : motor if off ..., so we don't move */
|
|
/* if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
|
|
dev->scanhead_position_in_steps += move; */
|
|
|
|
/* white shading */
|
|
dev->scanhead_position_in_steps += move;
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 0, slope_table,
|
|
dev->calib_reg[reg_0x21].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_regs_for_shading: Failed to send slope table: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_bulk_write_register (dev, dev->calib_reg,
|
|
GENESYS_GL646_MAX_REGS * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_regs_for_shading: Failed to bulk write registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = gl646_set_fe (dev, AFE_SET);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_regs_for_shading: Failed to set frontend: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_init_register_for_shading: completed\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* set up registers for the actual scan
|
|
* todo: once we clearly figure out how to set up
|
|
* registers for every scan parameters, use
|
|
* it to improve register settings in calibration
|
|
* functions
|
|
*/
|
|
static SANE_Status
|
|
gl646_init_regs_for_scan (Genesys_Device * dev)
|
|
{
|
|
SANE_Bool same_speed = 0;
|
|
int dpiset;
|
|
int start, end, pixels;
|
|
int channels;
|
|
int words_per_line;
|
|
int move, lincnt;
|
|
int exposure_time;
|
|
int i, depth;
|
|
int slope_dpi = 0;
|
|
int move_dpi = 0;
|
|
int fast_dpi = 0;
|
|
int fast_exposure = 0;
|
|
int dummy;
|
|
u_int32_t steps_sum, z1, z2;
|
|
SANE_Bool half_ccd; /* false: full CCD res is used, true, half max CCD res is used */
|
|
SANE_Status status;
|
|
unsigned int stagger;
|
|
unsigned int max_shift;
|
|
float read_factor;
|
|
size_t requested_buffer_size;
|
|
size_t read_buffer_size;
|
|
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_scan settings:\nResolution: %ux%uDPI\n"
|
|
"Lines : %u\nPPL : %u\nStartpos : %.3f/%.3f\nScan mode : %d\n\n",
|
|
dev->settings.xres, dev->settings.yres, dev->settings.lines,
|
|
dev->settings.pixels, dev->settings.tl_x, dev->settings.tl_y,
|
|
dev->settings.scan_mode);
|
|
|
|
/* we have 2 domains for ccd: yres below or above half ccd max dpi */
|
|
/* for some reason, windows sets DPISET to twice the needed value,
|
|
we're doing the same for now, but maybe we could avoid this, which
|
|
would avoid cropping data in reads */
|
|
half_ccd = SANE_TRUE;
|
|
dpiset = dev->sensor.optical_res;
|
|
i = dev->sensor.optical_res / dev->settings.xres;
|
|
if (i <= 1)
|
|
half_ccd = SANE_FALSE;
|
|
if (i <= 3)
|
|
dpiset = dev->sensor.optical_res;
|
|
else if (i <= 5)
|
|
dpiset = dev->sensor.optical_res / 2;
|
|
else if (i <= 7)
|
|
dpiset = dev->sensor.optical_res / 3;
|
|
else if (i <= 11)
|
|
dpiset = dev->sensor.optical_res / 4;
|
|
else if (i <= 15)
|
|
dpiset = dev->sensor.optical_res / 6;
|
|
else if (i <= 23)
|
|
dpiset = dev->sensor.optical_res / 8;
|
|
else if (i <= 24)
|
|
dpiset = dev->sensor.optical_res / 12;
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_scan : i=%d, dpiset=%d, half_ccd=%d\n", i,
|
|
dpiset, half_ccd);
|
|
|
|
/* sigh ... MD6471 motor's provide vertically shifted columns at hi res ... */
|
|
if ((!half_ccd) && (dev->model->flags & GENESYS_FLAG_STAGGERED_LINE))
|
|
stagger = (4 * dev->settings.yres) / dev->motor.base_ydpi;
|
|
else
|
|
stagger = 0;
|
|
DBG (DBG_info, "gl646_init_regs_for_scan : stagger=%d lines\n", stagger);
|
|
|
|
/* compute scan parameters values */
|
|
/* pixels are allways given at half or full CCD optical resolution */
|
|
/* use detected left margin and fixed value */
|
|
start =
|
|
dev->sensor.CCD_start_xoffset +
|
|
(SANE_UNFIX (dev->model->x_offset) * dev->sensor.optical_res) /
|
|
MM_PER_INCH;
|
|
|
|
/* add x coordinates */
|
|
start += (dev->settings.tl_x * dev->sensor.optical_res) / MM_PER_INCH;
|
|
if (half_ccd)
|
|
start = start / 2;
|
|
if (stagger > 0)
|
|
start = start | 1;
|
|
|
|
/* compute correct pixels number */
|
|
read_factor = 0.0;
|
|
pixels =
|
|
(dev->settings.pixels * dev->sensor.optical_res) / dev->settings.xres;
|
|
if (half_ccd)
|
|
pixels = pixels / 2;
|
|
|
|
/* round up pixels number if needed */
|
|
if (dpiset != 2 * dev->settings.xres
|
|
&& dev->settings.xres < dev->sensor.optical_res)
|
|
{
|
|
i = dev->sensor.optical_res / dpiset;
|
|
pixels = ((pixels + i - 1) / i) * i;
|
|
/* sets flag for reading */
|
|
/* the 2 factor is due to the fact that dpiset = 2 desired ccd dpi */
|
|
read_factor = (float) pixels / (float) (dev->settings.pixels * 2);
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_scan : rounding up pixels, factor=%f\n",
|
|
read_factor);
|
|
}
|
|
|
|
end = start + pixels;
|
|
|
|
/* set line size */
|
|
if (dev->settings.scan_mode == 4) /* single pass color */
|
|
channels = 3;
|
|
else
|
|
channels = 1;
|
|
|
|
/* scanned area must be enlarged by max color shift needed */
|
|
/* all values are assumed >= 0 */
|
|
if (channels > 1)
|
|
{
|
|
max_shift = dev->model->ld_shift_r;
|
|
if ((unsigned int) dev->model->ld_shift_b > max_shift)
|
|
max_shift = dev->model->ld_shift_b;
|
|
if ((unsigned int) dev->model->ld_shift_g > max_shift)
|
|
max_shift = dev->model->ld_shift_g;
|
|
max_shift = (max_shift * dev->settings.yres) / dev->motor.base_ydpi;
|
|
}
|
|
else
|
|
{
|
|
max_shift = 0;
|
|
}
|
|
|
|
/* enable shading */
|
|
dev->reg[reg_0x01].value |= REG01_DVDSET;
|
|
dev->reg[reg_0x01].value &= ~REG01_FASTMOD;
|
|
|
|
dev->reg[reg_0x02].value = REG02_NOTHOME | REG02_MTRPWR;
|
|
|
|
/* if dpi is low enough, we don't need to use 2 tables moving */
|
|
if (dev->settings.yres > dev->sensor.optical_res / 4)
|
|
dev->reg[reg_0x02].value |= REG02_FASTFED;
|
|
|
|
/* motor speed */
|
|
dev->reg[reg_0x02].value &= ~REG02_STEPSEL;
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
if (half_ccd)
|
|
dev->reg[reg_0x02].value |= REG02_HALFSTEP;
|
|
else
|
|
dev->reg[reg_0x02].value |= REG02_QUATERSTEP;
|
|
}
|
|
else
|
|
dev->reg[reg_0x02].value |= REG02_FULLSTEP;
|
|
|
|
/* average looks better than deletion */
|
|
dev->reg[reg_0x03].value |= REG03_AVEENB;
|
|
|
|
/* monochrome / color scan */
|
|
dev->reg[reg_0x04].value &= ~(REG04_FILTER | REG04_BITSET);
|
|
/* we could make the color filter used an advanced option of the backend */
|
|
if (channels == 1)
|
|
dev->reg[reg_0x04].value |= 0x08; /* green filter */
|
|
|
|
/* bit depth: 8bit + gamma, 16 bits + no gamma */
|
|
if (dev->settings.scan_mode == 0)
|
|
{
|
|
/* lineart, todo : find logs of real lineart, not an emulated one
|
|
with gray level scanning */
|
|
depth = 8;
|
|
dev->reg[reg_0x04].value &= ~REG04_LINEART;
|
|
}
|
|
else
|
|
{
|
|
if (dev->settings.depth > 8)
|
|
{
|
|
depth = 16;
|
|
dev->reg[reg_0x04].value |= REG04_BITSET;
|
|
}
|
|
else
|
|
depth = 8;
|
|
dev->reg[reg_0x04].value &= ~REG04_LINEART;
|
|
}
|
|
|
|
/* it is useles to scan at 16 bit if gamma table is at 12 or 14 bits
|
|
so we disable hardware gamma correction when doing 16 bits scans */
|
|
if (dev->settings.depth < 16)
|
|
dev->reg[reg_0x05].value |= REG05_GMMENB;
|
|
else
|
|
dev->reg[reg_0x05].value &= ~REG05_GMMENB;
|
|
|
|
/* sensor parameters */
|
|
gl646_setup_sensor (dev, dev->reg, 1, half_ccd);
|
|
|
|
/* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1
|
|
dummy line. Maybe the dummy line adds correctness since the motor runs
|
|
slower (higher dpi) */
|
|
dev->reg[reg_0x1e].value = dev->reg[reg_0x1e].value & 0xf0; /* 0 dummy lines */
|
|
dummy = 0;
|
|
if (dev->model->ccd_type == CCD_5345)
|
|
{
|
|
if (dpiset >= dev->sensor.optical_res / 3
|
|
&& dpiset <= dev->sensor.optical_res / 2)
|
|
dummy = 1;
|
|
if (dev->settings.yres == 800)
|
|
dummy = 3;
|
|
}
|
|
dev->reg[reg_0x1e].value = dev->reg[reg_0x1e].value | dummy; /* dummy lines */
|
|
|
|
switch (dev->model->motor_type)
|
|
{
|
|
case MOTOR_5345:
|
|
gl646_setup_steps (dev, dev->reg, dev->settings.yres);
|
|
fast_dpi = 200;
|
|
fast_exposure = 3600;
|
|
slope_dpi = dev->settings.yres;
|
|
|
|
/* we use same_speed until below a threshold */
|
|
if (slope_dpi < dev->motor.base_ydpi / 2)
|
|
same_speed = 0;
|
|
else
|
|
same_speed = 1;
|
|
break;
|
|
|
|
case MOTOR_HP2400:
|
|
case MOTOR_HP2300:
|
|
gl646_setup_steps (dev, dev->reg, dev->settings.yres);
|
|
fast_dpi = dev->motor.base_ydpi / 4;
|
|
fast_exposure = 2700;
|
|
slope_dpi = dev->settings.yres;
|
|
same_speed = 0;
|
|
break;
|
|
}
|
|
if (slope_dpi == 75 && dev->model->motor_type == MOTOR_5345) /* todo: do not hard code this one */
|
|
slope_dpi = 100;
|
|
slope_dpi = slope_dpi * (1 + dummy);
|
|
|
|
lincnt = dev->settings.lines - 1 + max_shift + stagger;
|
|
|
|
dev->reg[reg_0x25].value = LOBYTE (HIWORD (lincnt)); /* scan line numbers - here one line */
|
|
dev->reg[reg_0x26].value = HIBYTE (LOWORD (lincnt));
|
|
dev->reg[reg_0x27].value = LOBYTE (LOWORD (lincnt));
|
|
|
|
if ((dev->model->ccd_type == CCD_5345) && (channels == 1) && (!half_ccd))
|
|
{
|
|
dev->reg[reg_0x28].value = HIBYTE (250);
|
|
dev->reg[reg_0x29].value = LOBYTE (250);
|
|
}
|
|
else
|
|
{
|
|
dev->reg[reg_0x28].value = HIBYTE (511);
|
|
dev->reg[reg_0x29].value = LOBYTE (511);
|
|
}
|
|
|
|
|
|
dev->reg[reg_0x2c].value = HIBYTE (dpiset);
|
|
dev->reg[reg_0x2d].value = LOBYTE (dpiset);
|
|
|
|
dev->reg[reg_0x30].value = HIBYTE (start);
|
|
dev->reg[reg_0x31].value = LOBYTE (start);
|
|
dev->reg[reg_0x32].value = HIBYTE (end);
|
|
dev->reg[reg_0x33].value = LOBYTE (end);
|
|
|
|
if (half_ccd)
|
|
words_per_line = (dpiset * pixels * channels) / dev->sensor.optical_res;
|
|
else
|
|
words_per_line = pixels * channels;
|
|
words_per_line = (words_per_line * depth) / 8;
|
|
|
|
dev->reg[reg_0x35].value = LOBYTE (HIWORD (words_per_line));
|
|
dev->reg[reg_0x36].value = HIBYTE (LOWORD (words_per_line));
|
|
dev->reg[reg_0x37].value = LOBYTE (LOWORD (words_per_line));
|
|
|
|
if ((dev->model->ccd_type == CCD_5345)
|
|
|| (dev->model->ccd_type == CCD_HP2300))
|
|
exposure_time =
|
|
sanei_genesys_exposure_time (dev, dev->reg, dev->settings.xres);
|
|
else
|
|
exposure_time = dev->settings.exposure_time;
|
|
|
|
dev->reg[reg_0x38].value = HIBYTE (exposure_time);
|
|
dev->reg[reg_0x39].value = LOBYTE (exposure_time);
|
|
|
|
/* build slope table for choosen dpi */
|
|
sanei_genesys_create_slope_table (dev,
|
|
dev->slope_table0,
|
|
dev->reg[reg_0x21].value,
|
|
dev->reg[reg_0x02].value & REG02_STEPSEL,
|
|
exposure_time, same_speed, slope_dpi);
|
|
|
|
/* computes sum of used steps */
|
|
steps_sum = 0;
|
|
for (i = 0; i < dev->reg[reg_0x1f].value - 1; i++)
|
|
steps_sum += dev->slope_table0[i];
|
|
|
|
/* build slope1 (fast moving) */
|
|
sanei_genesys_create_slope_table (dev,
|
|
dev->slope_table1,
|
|
dev->reg[reg_0x6b].value,
|
|
(dev->reg[reg_0x6a].
|
|
value & REG6A_FSTPSEL) >> 6,
|
|
fast_exposure, 0, fast_dpi);
|
|
|
|
/* steps to move to reach scanning area:
|
|
- first we move to physical start of scanning
|
|
either by a fixed steps amount from the black strip
|
|
or by a fixed amount from parking position,
|
|
minus the steps done during shading calibration
|
|
- then we move by the needed offset whitin physical
|
|
scanning area
|
|
- todo: substract steps done during motor acceleration or
|
|
will it be included in y_offset ?
|
|
|
|
assumption: steps are expressed at maximum motor resolution
|
|
|
|
we need:
|
|
SANE_Fixed y_offset;
|
|
SANE_Fixed y_size;
|
|
SANE_Fixed y_offset_calib;
|
|
mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */
|
|
|
|
/* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is
|
|
relative from origin, else, it is from parking position */
|
|
switch (dev->model->motor_type)
|
|
{
|
|
case MOTOR_5345:
|
|
move_dpi = dev->motor.optical_ydpi;
|
|
break;
|
|
default:
|
|
move_dpi = dev->motor.base_ydpi;
|
|
break;
|
|
}
|
|
if (dev->model->flags & GENESYS_FLAG_SEARCH_START)
|
|
move = (SANE_UNFIX (dev->model->y_offset_calib) * move_dpi) / MM_PER_INCH;
|
|
else
|
|
move = 0;
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move);
|
|
|
|
move += (SANE_UNFIX (dev->model->y_offset) * move_dpi) / MM_PER_INCH;
|
|
if (move < 0)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_regs_for_scan: overriding negative move value %d\n",
|
|
move);
|
|
move = 1;
|
|
}
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move);
|
|
|
|
/* add tl_y to base movement */
|
|
/* move += (dev->settings.tl_y * dev->motor.optical_ydpi) / MM_PER_INCH; */
|
|
move += (dev->settings.tl_y * move_dpi) / MM_PER_INCH;
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move);
|
|
|
|
/* substract current head position */
|
|
move -= dev->scanhead_position_in_steps - stagger;
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move);
|
|
|
|
if ((dev->reg[reg_0x02].value & REG02_FASTFED)
|
|
&& (dev->model->motor_type == MOTOR_HP2300))
|
|
{
|
|
move *= 2;
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move);
|
|
}
|
|
|
|
/* round it */
|
|
move = ((move + dummy) / (dummy + 1)) * (dummy + 1);
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move);
|
|
|
|
/* set feed steps number of motor move */
|
|
dev->reg[reg_0x3d].value = LOBYTE (HIWORD (move));
|
|
dev->reg[reg_0x3e].value = HIBYTE (LOWORD (move));
|
|
dev->reg[reg_0x3f].value = LOBYTE (LOWORD (move));
|
|
|
|
sanei_genesys_calculate_zmode2 (dev->reg[reg_0x02].value & REG02_FASTFED,
|
|
exposure_time,
|
|
dev->slope_table0,
|
|
dev->reg[reg_0x21].value,
|
|
move, dev->reg[reg_0x22].value, &z1, &z2);
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: z1 = %d\n", z1);
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: z2 = %d\n", z2);
|
|
dev->reg[reg_0x60].value = HIBYTE (z1);
|
|
dev->reg[reg_0x61].value = LOBYTE (z1);
|
|
dev->reg[reg_0x62].value = HIBYTE (z2);
|
|
dev->reg[reg_0x63].value = LOBYTE (z2);
|
|
dev->reg[reg_0x6c].value = (dev->reg[reg_0x6c].value & REG6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16) & 0x07); /* todo: double check */
|
|
|
|
/* todo: is it really that useful ? */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
if (dev->settings.yres > 150)
|
|
dev->reg[reg_0x65].value = 0;
|
|
else
|
|
dev->reg[reg_0x65].value = 2;
|
|
}
|
|
else
|
|
dev->reg[reg_0x65].value = 0x3f;
|
|
dev->reg[reg_0x67].value = dev->gpo.enable[1];
|
|
|
|
/* prepares data reordering */
|
|
|
|
/* we must use a round number of words_per_line */
|
|
requested_buffer_size = (BULKIN_MAXSIZE / words_per_line) * words_per_line;
|
|
|
|
read_buffer_size =
|
|
2 * requested_buffer_size +
|
|
((max_shift + stagger) * pixels * channels * depth) / 8;
|
|
|
|
RIE (sanei_genesys_buffer_free (&(dev->read_buffer)));
|
|
RIE (sanei_genesys_buffer_alloc (&(dev->read_buffer), read_buffer_size));
|
|
|
|
RIE (sanei_genesys_buffer_free (&(dev->lines_buffer)));
|
|
RIE (sanei_genesys_buffer_alloc (&(dev->lines_buffer), read_buffer_size));
|
|
|
|
RIE (sanei_genesys_buffer_free (&(dev->shrink_buffer)));
|
|
RIE (sanei_genesys_buffer_alloc (&(dev->shrink_buffer),
|
|
requested_buffer_size));
|
|
|
|
RIE (sanei_genesys_buffer_free (&(dev->out_buffer)));
|
|
RIE (sanei_genesys_buffer_alloc (&(dev->out_buffer),
|
|
(8 * dev->settings.pixels * channels *
|
|
depth) / 8));
|
|
|
|
/* scan bytes to read */
|
|
dev->read_bytes_left = words_per_line * (lincnt + 1);
|
|
|
|
|
|
DBG (DBG_info,
|
|
"gl646_init_regs_for_scan: physical bytes to read = %lu\n",
|
|
(u_long) dev->read_bytes_left);
|
|
dev->read_active = SANE_TRUE;
|
|
|
|
|
|
dev->current_setup.pixels = (pixels * dpiset) / dev->sensor.optical_res;
|
|
dev->current_setup.lines = lincnt + 1;
|
|
dev->current_setup.depth = depth;
|
|
dev->current_setup.channels = channels;
|
|
dev->current_setup.exposure_time = exposure_time;
|
|
if (half_ccd)
|
|
dev->current_setup.xres = dpiset / 2;
|
|
else
|
|
dev->current_setup.xres = dpiset;
|
|
dev->current_setup.yres = dev->settings.yres;
|
|
dev->current_setup.half_ccd = half_ccd;
|
|
dev->current_setup.stagger = stagger;
|
|
dev->current_setup.max_shift = max_shift + stagger;
|
|
|
|
|
|
/* TODO: should this be done elsewhere? */
|
|
/* scan bytes to send to the frontend */
|
|
/* theory :
|
|
target_size =
|
|
(dev->settings.pixels * dev->settings.lines * channels * depth) / 8;
|
|
but it suffers from integer overflow so we do the following:
|
|
|
|
1 bit color images store color data byte-wise, eg byte 0 contains
|
|
8 bits of red data, byte 1 contains 8 bits of green, byte 2 contains
|
|
8 bits of blue.
|
|
This does not fix the overflow, though.
|
|
644mp*16 = 10gp, leading to an overflow
|
|
-- pierre
|
|
*/
|
|
|
|
dev->total_bytes_read = 0;
|
|
if (depth == 1 || dev->settings.scan_mode == 0)
|
|
dev->total_bytes_to_read =
|
|
((dev->settings.pixels * dev->settings.lines) / 8 +
|
|
(((dev->settings.pixels * dev->settings.lines) % 8) ? 1 : 0)) *
|
|
channels;
|
|
else
|
|
dev->total_bytes_to_read =
|
|
dev->settings.pixels * dev->settings.lines * channels * (depth / 8);
|
|
|
|
DBG (DBG_info, "gl646_init_regs_for_scan: total bytes to send = %lu\n",
|
|
(u_long) dev->total_bytes_to_read);
|
|
/* END TODO */
|
|
|
|
|
|
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 0, dev->slope_table0,
|
|
sanei_genesys_read_reg_from_set (dev->reg, 0x21));
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_regs_for_scan: failed to send slope table 0: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 1, dev->slope_table1,
|
|
sanei_genesys_read_reg_from_set (dev->reg, 0x6b));
|
|
|
|
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init_regs_for_scan: failed to send slope table 1: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_init_regs_for_scan: completed\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
* this function sends generic gamma table (ie linear ones)
|
|
* or the Sensor specific one if provided
|
|
*/
|
|
static SANE_Status
|
|
gl646_send_gamma_table (Genesys_Device * dev, SANE_Bool generic)
|
|
{
|
|
int size;
|
|
int address;
|
|
int status;
|
|
u_int8_t *gamma;
|
|
int i;
|
|
|
|
/* don't send anything if no specific gamma table defined */
|
|
if (!generic
|
|
&& (dev->sensor.red_gamma_table == NULL
|
|
|| dev->sensor.green_gamma_table == NULL
|
|
|| dev->sensor.blue_gamma_table == NULL))
|
|
{
|
|
DBG (DBG_proc, "gl646_send_gamma_table: nothing to send, skipping\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* gamma table size */
|
|
if (dev->reg[reg_0x05].value & REG05_GMMTYPE)
|
|
size = 16384;
|
|
else
|
|
size = 4096;
|
|
|
|
/* table address */
|
|
switch (dev->reg[reg_0x05].value >> 6)
|
|
{
|
|
case 0: /* 600 dpi */
|
|
address = 0x09000;
|
|
break;
|
|
case 1: /* 1200 dpi */
|
|
address = 0x11000;
|
|
break;
|
|
case 2: /* 2400 dpi */
|
|
address = 0x20000;
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* allocate temporary gamma tables: 16 bits words, 3 channels */
|
|
gamma = (u_int8_t *) malloc (size * 2 * 3);
|
|
if (!gamma)
|
|
return SANE_STATUS_NO_MEM;
|
|
/* take care off generic/specific data */
|
|
if (generic)
|
|
{
|
|
/* fill with default values */
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
gamma[i * 2] = i & 0xff;
|
|
gamma[i * 2 + 1] = i >> 8;
|
|
gamma[i * 2 + size * 2] = i & 0xff;
|
|
gamma[i * 2 + 1 + size * 2] = i >> 8;
|
|
gamma[i * 2 + size * 4] = i & 0xff;
|
|
gamma[i * 2 + 1 + size * 4] = i >> 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* copy sensor specific's gamma tables */
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
gamma[i * 2] = dev->sensor.red_gamma_table[i] & 0xff;
|
|
gamma[i * 2 + 1] = dev->sensor.red_gamma_table[i] >> 8;
|
|
gamma[i * 2 + size * 2] = dev->sensor.green_gamma_table[i] & 0xff;
|
|
gamma[i * 2 + 1 + size * 2] = dev->sensor.green_gamma_table[i] >> 8;
|
|
gamma[i * 2 + size * 4] = dev->sensor.blue_gamma_table[i] & 0xff;
|
|
gamma[i * 2 + 1 + size * 4] = dev->sensor.blue_gamma_table[i] >> 8;
|
|
}
|
|
}
|
|
|
|
/* send address */
|
|
status = sanei_genesys_set_buffer_address (dev, address);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (gamma);
|
|
DBG (DBG_error,
|
|
"gl646_send_gamma_table: failed to set buffer address: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* send data */
|
|
status =
|
|
gl646_bulk_write_data (dev, 0x3c, (u_int8_t *) gamma, size * 2 * 3);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (gamma);
|
|
DBG (DBG_error,
|
|
"gl646_send_gamma_table: failed to send gamma table: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
DBG (DBG_proc, "gl646_send_gamma_table: completed\n");
|
|
free (gamma);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* this function does the led calibration.
|
|
*/
|
|
static SANE_Status
|
|
gl646_led_calibration (Genesys_Device * dev)
|
|
{
|
|
DBG (DBG_error, "Implementation for led calibration missing\n");
|
|
if(dev || dev==NULL)
|
|
return SANE_STATUS_INVAL;
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* this function does the offset calibration by scanning one line of the calibration
|
|
area below scanner's top. There is a black margin and the remaining is white.
|
|
genesys_search_start() must have been called so that the offsets and margins
|
|
are allready known.
|
|
*/
|
|
static SANE_Status
|
|
gl646_offset_calibration (Genesys_Device * dev)
|
|
{
|
|
int num_pixels;
|
|
int total_size;
|
|
int avg[3];
|
|
u_int8_t *first_line, *second_line;
|
|
int i, j;
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
int average, val, count;
|
|
int minimum, offset, dpi, channels;
|
|
SANE_Bool half_ccd = 1;
|
|
int steps = 0, lincnt = 1, start_pixel;
|
|
u_int16_t slope_table[256];
|
|
|
|
DBG (DBG_proc, "gl646_offset_calibration\n");
|
|
|
|
/* offset calibration is allways done in color mode */
|
|
channels = 3;
|
|
|
|
/* todo : turn it into a switch */
|
|
/* full CCD width is used */
|
|
if (dev->model->ccd_type == CCD_5345)
|
|
{
|
|
if (dev->settings.xres > dev->sensor.optical_res / 2)
|
|
{
|
|
half_ccd = 0;
|
|
num_pixels = dev->sensor.sensor_pixels;
|
|
}
|
|
else
|
|
{
|
|
half_ccd = 1;
|
|
num_pixels = dev->sensor.sensor_pixels / 2;
|
|
}
|
|
dpi = 1200;
|
|
steps = 0;
|
|
lincnt = 1;
|
|
}
|
|
else if (dev->model->ccd_type == CCD_HP2300)
|
|
{
|
|
dpi = dev->settings.xres;
|
|
steps = 1;
|
|
lincnt = 2;
|
|
num_pixels = 2668;
|
|
if (dev->settings.xres > dev->sensor.optical_res / 2)
|
|
half_ccd = 0;
|
|
else
|
|
half_ccd = 1;
|
|
}
|
|
else
|
|
{
|
|
dpi = 600;
|
|
num_pixels = 2 * dpi;
|
|
lincnt = 1;
|
|
}
|
|
start_pixel = dev->sensor.dummy_pixel;
|
|
|
|
total_size = num_pixels * channels * 2 * lincnt; /* colors * bytes_per_color * scan lines */
|
|
|
|
dev->calib_reg[reg_0x01].value &= ~REG01_DVDSET;
|
|
dev->calib_reg[reg_0x02].value = REG02_ACDCDIS;
|
|
if (dev->model->motor_type == MOTOR_5345 || MOTOR_HP2300)
|
|
{
|
|
if (half_ccd)
|
|
dev->calib_reg[reg_0x02].value =
|
|
(dev->calib_reg[reg_0x02].value & ~REG02_STEPSEL) | REG02_HALFSTEP;
|
|
else
|
|
dev->calib_reg[reg_0x02].value =
|
|
(dev->calib_reg[reg_0x02].
|
|
value & ~REG02_STEPSEL) | REG02_QUATERSTEP;
|
|
if (dev->model->motor_type == CCD_5345)
|
|
dev->calib_reg[reg_0x03].value |= REG03_AVEENB;
|
|
else
|
|
dev->calib_reg[reg_0x03].value &= ~REG03_AVEENB;
|
|
}
|
|
|
|
dev->calib_reg[reg_0x04].value =
|
|
(dev->calib_reg[reg_0x04].
|
|
value & ~REG04_LINEART & ~REG04_FILTER) | REG04_BITSET;
|
|
if (channels == 1)
|
|
dev->calib_reg[reg_0x04].value |= 0x08; /* green filter */
|
|
|
|
dev->calib_reg[reg_0x05].value = (dev->calib_reg[reg_0x05].value & ~REG05_GMMENB); /* disable gamma */
|
|
|
|
/* ST12: 0x01 0x00 0x02 0x41 0x03 0x1f 0x04 0x53 0x05 0x10 0x06 0x10 0x08 0x02 0x09 0x00 0x0a 0x06 0x0b 0x04 */
|
|
/* ST24: 0x02 0x71 0x04 0x5f 0x05 0x50 0x08 0x0e 0x09 0x0c 0x0a 0x00 0x0b 0x0c */
|
|
#if 0
|
|
dev->calib_reg[reg_0x01].value = 0x00 /*0x02 */ ; /* disable shading, enable CCD, color, 1M */
|
|
dev->calib_reg[reg_0x02].value = 0x71 /*0x40 */ ; /* no forward/backward, no auto-home, motor off, full step */
|
|
dev->calib_reg[reg_0x03].value = 0x1f /*0x17 */ ; /* lamp on */
|
|
dev->calib_reg[reg_0x04].value = 0x53; /* 16 bits data, 16 bits A/D, color, Wolfson fe */
|
|
dev->calib_reg[reg_0x05].value = 0x50 /*0x00 */ ; /* CCD res = 600 Dpi, 12 bits gamma, disable gamma, 24 clocks/pixel */
|
|
dev->calib_reg[reg_0x06].value = 0x10; /* power bit set, shading gain = 4 times system, no asic test */
|
|
dev->calib_reg[reg_0x07].value = 0x00; /* DMA access */
|
|
#endif
|
|
|
|
gl646_setup_sensor (dev, dev->calib_reg, 1, half_ccd);
|
|
|
|
/* motor & movement control , todo : could be removed */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
dev->calib_reg[reg_0x1f].value = 0x01; /* scan feed step for table one in two table mode only */
|
|
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
gl646_setup_steps (dev, dev->calib_reg, dpi);
|
|
dev->calib_reg[reg_0x21].value = 1;
|
|
}
|
|
else
|
|
{
|
|
dev->calib_reg[reg_0x21].value = 2;
|
|
dev->calib_reg[reg_0x22].value = 16;
|
|
dev->calib_reg[reg_0x23].value = 16;
|
|
dev->calib_reg[reg_0x24].value = 2;
|
|
}
|
|
|
|
dev->calib_reg[reg_0x25].value = LOBYTE (HIWORD (lincnt));
|
|
dev->calib_reg[reg_0x26].value = HIBYTE (LOWORD (lincnt));
|
|
dev->calib_reg[reg_0x27].value = LOBYTE (LOWORD (lincnt));
|
|
|
|
dev->calib_reg[reg_0x28].value = 0x01; /* PWM duty for lamp control */
|
|
dev->calib_reg[reg_0x29].value = 0xff;
|
|
|
|
dev->calib_reg[reg_0x2c].value = HIBYTE (dpi);
|
|
dev->calib_reg[reg_0x2d].value = LOBYTE (dpi);
|
|
|
|
dev->calib_reg[reg_0x30].value = HIBYTE (start_pixel);
|
|
dev->calib_reg[reg_0x31].value = LOBYTE (start_pixel);
|
|
dev->calib_reg[reg_0x32].value = HIBYTE (start_pixel + num_pixels);
|
|
dev->calib_reg[reg_0x33].value = LOBYTE (start_pixel + num_pixels);
|
|
if (dev->model->ccd_type == CCD_5345)
|
|
{
|
|
/* full CCD width from 1st non dummy pixel */
|
|
total_size = num_pixels * channels * 2 * lincnt;
|
|
|
|
/* fixed maximum word number */
|
|
dev->calib_reg[reg_0x35].value = LOBYTE (HIWORD (65536));
|
|
dev->calib_reg[reg_0x36].value = HIBYTE (LOWORD (65536));
|
|
dev->calib_reg[reg_0x37].value = LOBYTE (LOWORD (65536));
|
|
}
|
|
else
|
|
{
|
|
total_size =
|
|
(num_pixels * channels * 2 * dpi * lincnt) / dev->sensor.optical_res;
|
|
dev->calib_reg[reg_0x35].value = LOBYTE (HIWORD (total_size / lincnt));
|
|
dev->calib_reg[reg_0x36].value = HIBYTE (LOWORD (total_size / lincnt));
|
|
dev->calib_reg[reg_0x37].value = LOBYTE (LOWORD (total_size / lincnt));
|
|
}
|
|
|
|
dev->calib_reg[reg_0x34].value = dev->sensor.dummy_pixel; /* set the CCD dummy & optical black pixels number, for whole line shading (64) */
|
|
dev->calib_reg[reg_0x38].value =
|
|
HIBYTE (sanei_genesys_exposure_time
|
|
(dev, dev->calib_reg, dev->settings.xres));
|
|
dev->calib_reg[reg_0x39].value =
|
|
LOBYTE (sanei_genesys_exposure_time
|
|
(dev, dev->calib_reg, dev->settings.xres));
|
|
|
|
/* set feed steps number of motor move */
|
|
dev->calib_reg[reg_0x3d].value = LOBYTE (HIWORD (steps));
|
|
dev->calib_reg[reg_0x3e].value = HIBYTE (LOWORD (steps));
|
|
dev->calib_reg[reg_0x3f].value = LOBYTE (LOWORD (steps));
|
|
|
|
/*
|
|
ST12: 0x60 0x00 0x61 0x00 0x62 0x00 0x63 0x00 0x64 0x00 0x65 0x3f 0x66 0x00 0x67 0x00 0x68 0x51 0x69 0x20 */
|
|
/* ST24: 0x60 0x00 0x61 0x00 0x62 0x00 0x63 0x00 0x64 0x00 0x65 0x00 0x66 0x00 0x67 0x00 0x68 0x51 0x69 0x20 */
|
|
dev->calib_reg[reg_0x60].value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
|
|
dev->calib_reg[reg_0x61].value = 0x00; /* (21h+22h)/LPeriod */
|
|
dev->calib_reg[reg_0x62].value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
|
|
dev->calib_reg[reg_0x63].value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
|
|
dev->calib_reg[reg_0x64].value = 0x00; /* motor PWM frequency */
|
|
dev->calib_reg[reg_0x65].value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
dev->gpo.value[1] |= 0x18; /* has to do with bipolar V-ref */
|
|
if (dev->settings.yres <= 150)
|
|
dev->calib_reg[reg_0x65].value = 0x02;
|
|
}
|
|
dev->calib_reg[reg_0x66].value = dev->gpo.value[0];
|
|
dev->calib_reg[reg_0x67].value = dev->gpo.value[1];
|
|
dev->calib_reg[reg_0x68].value = dev->gpo.enable[0];
|
|
dev->calib_reg[reg_0x69].value = dev->gpo.enable[1];
|
|
|
|
/* ST12: 0x6a 0x7f 0x6b 0xff 0x6c 0x00 0x6d 0x01 */
|
|
/* ST24: 0x6a 0x40 0x6b 0xff 0x6c 0x00 0x6d 0x01 */
|
|
dev->calib_reg[reg_0x6c].value = 0x00; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
|
|
dev->calib_reg[reg_0x6a].value = dev->calib_reg[reg_0x6a].value | 0x02;
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
dev->calib_reg[reg_0x6b].value = 0xff;
|
|
dev->calib_reg[reg_0x6d].value = 0x41;
|
|
}
|
|
|
|
dev->calib_reg[reg_0x60].value = HIBYTE (0);
|
|
dev->calib_reg[reg_0x61].value = LOBYTE (0);
|
|
dev->calib_reg[reg_0x62].value = HIBYTE (0);
|
|
dev->calib_reg[reg_0x63].value = LOBYTE (0);
|
|
|
|
if (dev->model->motor_type == MOTOR_HP2300)
|
|
{
|
|
dev->calib_reg[reg_0x65].value = 0x3f; /* PWM duty cycle for table one motor phase (63 = max) */
|
|
dev->calib_reg[reg_0x6b].value = 0x02;
|
|
dev->calib_reg[reg_0x6d].value = 0x7f;
|
|
slope_table[0] = 4480;
|
|
slope_table[1] = 4480;
|
|
RIE (gl646_send_slope_table
|
|
(dev, 0, slope_table, dev->calib_reg[reg_0x21].value));
|
|
}
|
|
|
|
/* work around incorrect calibration when frequent color/dpi scan changes */
|
|
RIE (gl646_set_fe (dev, AFE_INIT));
|
|
|
|
RIE (gl646_set_fe (dev, AFE_SET));
|
|
|
|
RIE (gl646_bulk_write_register
|
|
(dev, dev->calib_reg, GENESYS_GL646_MAX_REGS * 2));
|
|
|
|
first_line = malloc (total_size);
|
|
if (!first_line)
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
second_line = malloc (total_size);
|
|
if (!second_line)
|
|
{
|
|
free (first_line);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
/* scan first line of data with no offset nor gain */
|
|
dev->frontend.gain[0] = 0x00;
|
|
dev->frontend.gain[1] = 0x00;
|
|
dev->frontend.gain[2] = 0x00;
|
|
offset = 0;
|
|
dev->frontend.offset[0] = offset;
|
|
dev->frontend.offset[1] = offset;
|
|
dev->frontend.offset[2] = offset;
|
|
status = gl646_set_fe (dev, AFE_SET);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (first_line);
|
|
free (second_line);
|
|
DBG (DBG_error,
|
|
"gl646_offset_calibration: failed to set frontend: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
RIE (gl646_begin_scan (dev, dev->calib_reg, SANE_TRUE));
|
|
RIE (sanei_genesys_read_data_from_scanner (dev, first_line, total_size));
|
|
RIE (gl646_end_scan (dev, dev->calib_reg, SANE_FALSE));
|
|
|
|
if (DBG_LEVEL >= DBG_data)
|
|
sanei_genesys_write_pnm_file ("offset1.pnm",
|
|
first_line,
|
|
16,
|
|
channels,
|
|
(num_pixels * dpi) /
|
|
dev->sensor.optical_res, lincnt);
|
|
|
|
/* We search for minimum dark value, then deduce a threshold from it.
|
|
This allow us to use data without knowing the geometry of the
|
|
black areas */
|
|
val = 0;
|
|
minimum = 65535;
|
|
for (j = 0; j < channels; j++)
|
|
{
|
|
for (i = 0; i < num_pixels; i++)
|
|
{
|
|
val =
|
|
first_line[i * 2 * channels + 2 * j + 1] * 256 +
|
|
first_line[i * 2 * channels + 2 * j];
|
|
if (val < minimum)
|
|
minimum = val;
|
|
}
|
|
}
|
|
/* minimum accepted black pixel */
|
|
minimum *= 1.1;
|
|
DBG (DBG_proc, "gl646_offset_calibration: black threshold = %d\n",
|
|
minimum);
|
|
|
|
/* computes lowest average black value on black margin */
|
|
for (j = 0; j < channels; j++)
|
|
{
|
|
avg[j] = 0;
|
|
count = 0;
|
|
for (i = 0; i < num_pixels; i++)
|
|
{
|
|
val =
|
|
first_line[i * 2 * channels + 2 * j + 1] * 256 +
|
|
first_line[i * 2 * channels + 2 * j];
|
|
if (val <= minimum)
|
|
{
|
|
avg[j] += val;
|
|
count++;
|
|
}
|
|
}
|
|
if (count)
|
|
avg[j] /= count;
|
|
DBG (DBG_proc, "gl646_offset_calibration: avg[%d] = %d\n", j,
|
|
avg[j] / 256);
|
|
}
|
|
|
|
/* now finds minimal black average */
|
|
minimum = avg[0];
|
|
if (channels > 1)
|
|
{
|
|
if (avg[1] < minimum)
|
|
minimum = avg[1];
|
|
if (avg[2] < minimum)
|
|
minimum = avg[2];
|
|
}
|
|
|
|
switch (dev->model->ccd_type)
|
|
{
|
|
case CCD_5345:
|
|
offset = minimum / 256;
|
|
break;
|
|
case CCD_HP2300:
|
|
case CCD_HP2400:
|
|
offset = minimum / 2 * 256;
|
|
}
|
|
DBG (DBG_proc, "gl646_offset_calibration: minimum = %.2f, offset = %d\n",
|
|
(float) minimum / 256.0, offset);
|
|
|
|
/* scan second line of data with a fixed offset and no gain */
|
|
dev->frontend.offset[0] = offset;
|
|
dev->frontend.offset[1] = offset;
|
|
dev->frontend.offset[2] = offset;
|
|
status = gl646_set_fe (dev, AFE_SET);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (first_line);
|
|
free (second_line);
|
|
DBG (DBG_error,
|
|
"gl646_offset_calibration: failed to set frontend: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
RIE (gl646_begin_scan (dev, dev->calib_reg, SANE_TRUE));
|
|
RIE (sanei_genesys_read_data_from_scanner (dev, second_line, total_size));
|
|
RIE (gl646_end_scan (dev, dev->calib_reg, SANE_FALSE));
|
|
|
|
if (DBG_LEVEL >= DBG_data)
|
|
sanei_genesys_write_pnm_file ("offset2.pnm",
|
|
second_line,
|
|
16,
|
|
channels,
|
|
(num_pixels * dpi) /
|
|
dev->sensor.optical_res, lincnt);
|
|
|
|
/* compute final offsets */
|
|
for (j = 0; j < channels; j++)
|
|
{
|
|
/* finds minimum for current color channel */
|
|
minimum = 65535;
|
|
for (i = 0; i < num_pixels; i++)
|
|
{
|
|
val =
|
|
second_line[i * 2 * channels + 2 * j + 1] * 256 +
|
|
second_line[i * 2 * channels + 2 * j];
|
|
if (val < minimum)
|
|
minimum = val;
|
|
}
|
|
minimum *= 1.1;
|
|
|
|
/* computes black average for channel */
|
|
average = 0;
|
|
count = 0;
|
|
for (i = 0; i < num_pixels; i++)
|
|
{
|
|
val =
|
|
256 * second_line[i * 2 * channels + j * 2 + 1] +
|
|
second_line[i * 2 * channels + j * 2];
|
|
if (val < minimum)
|
|
{
|
|
average += val;
|
|
count++;
|
|
}
|
|
}
|
|
if (count)
|
|
average /= count;
|
|
|
|
switch (dev->model->ccd_type)
|
|
{
|
|
case CCD_5345:
|
|
/* dev->frontend.offset[j] = (offset*min[j]) / (min[j]-average); */
|
|
/* we assume a linear law, the 0.95 coeff is there because while we want
|
|
a DC offset of 0, values are varying a little and we let a little offset
|
|
to get sure we don't have "negative" values */
|
|
dev->frontend.offset[j] =
|
|
offset + ((float) (average * offset) * 0.95) / (avg[j] - average);
|
|
/* dev->frontend.offset[j] = offset + ((float) average) / (1.3 * 256) ; */
|
|
break;
|
|
case CCD_HP2300:
|
|
if (j == 0)
|
|
dev->frontend.offset[j] = offset + average / (1.2 * 256);
|
|
else
|
|
dev->frontend.offset[j] = offset + average / (1.0 * 256);
|
|
break;
|
|
}
|
|
DBG (DBG_proc,
|
|
"gl646_offset_calibration: average[%d] = %.2f, offset = %d\n", j,
|
|
(float) average / 256, dev->frontend.offset[j]);
|
|
}
|
|
if (channels == 1)
|
|
{
|
|
dev->frontend.offset[1] = dev->frontend.offset[0];
|
|
dev->frontend.offset[2] = dev->frontend.offset[0];
|
|
}
|
|
|
|
/* cleanup before return */
|
|
free (first_line);
|
|
free (second_line);
|
|
DBG (DBG_proc, "gl646_offset_calibration: completed\n");
|
|
return status;
|
|
}
|
|
|
|
/* alternative coarse gain calibration
|
|
this on uses the settings from offset_calibration and
|
|
uses only one scanline
|
|
*/
|
|
static SANE_Status
|
|
gl646_coarse_gain_calibration (Genesys_Device * dev, int dpi)
|
|
{
|
|
int num_pixels;
|
|
int black_pixels;
|
|
int total_size;
|
|
u_int8_t *line;
|
|
int i, j, channels;
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
float average[3];
|
|
int lincnt, words, count, val;
|
|
int maximum;
|
|
|
|
DBG (DBG_proc, "gl646_coarse_gain_calibration\n");
|
|
|
|
/* coarse gain calibration is allways done in color mode */
|
|
channels = 3;
|
|
|
|
black_pixels =
|
|
(dev->sensor.CCD_start_xoffset * dpi) / dev->sensor.optical_res;
|
|
|
|
lincnt = 65536 * dev->calib_reg[reg_0x25].value;
|
|
lincnt += 256 * dev->calib_reg[reg_0x26].value;
|
|
lincnt += dev->calib_reg[reg_0x27].value;
|
|
|
|
words = 65536 * dev->calib_reg[reg_0x35].value;
|
|
words += 256 * dev->calib_reg[reg_0x36].value;
|
|
words += dev->calib_reg[reg_0x37].value;
|
|
|
|
num_pixels = words / (channels * 2);
|
|
|
|
total_size = words * lincnt; /* colors * bytes_per_color * scan lines */
|
|
|
|
line = malloc (total_size);
|
|
if (!line)
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
/* sends offsets computed by offset calibration */
|
|
status = gl646_set_fe (dev, AFE_SET);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
free (line);
|
|
DBG (DBG_error,
|
|
"gl646_coarse_gain_calibration: failed to set frontend: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
RIE (gl646_begin_scan (dev, dev->calib_reg, SANE_TRUE));
|
|
RIE (sanei_genesys_read_data_from_scanner (dev, line, total_size));
|
|
RIE (gl646_end_scan (dev, dev->calib_reg, SANE_FALSE));
|
|
|
|
if (DBG_LEVEL >= DBG_data)
|
|
sanei_genesys_write_pnm_file ("alternative_coarse.pnm", line, 16,
|
|
channels, num_pixels, lincnt);
|
|
|
|
/* average high level for each channel and compute gain
|
|
to reach the target code
|
|
we only use the central half of the CCD data */
|
|
/* todo : use lincnt lines */
|
|
for (j = 0; j < channels; j++)
|
|
{
|
|
/* we find the maximum white value, so we can deduce a threshold
|
|
to average white values */
|
|
maximum = 0;
|
|
for (i = 0; i < num_pixels; i++)
|
|
{
|
|
val =
|
|
256 * line[i * 2 * channels + 2 * j + 1] + line[i * 2 * channels +
|
|
2 * j];
|
|
if (val > maximum)
|
|
maximum = val;
|
|
}
|
|
/* threshold */
|
|
maximum *= 0.9;
|
|
|
|
/* computes white average */
|
|
average[j] = 0;
|
|
count = 0;
|
|
for (i = 0; i < num_pixels; i++)
|
|
{
|
|
/* averaging anly white points allow us not to care about dark margins */
|
|
val =
|
|
256 * line[i * 2 * channels + 2 * j + 1] + line[i * 2 * channels +
|
|
2 * j];
|
|
if (val > maximum)
|
|
{
|
|
average[j] += val;
|
|
count++;
|
|
}
|
|
}
|
|
average[j] = average[j] / count;
|
|
|
|
switch (dev->model->ccd_type)
|
|
{
|
|
case CCD_HP2300:
|
|
dev->frontend.gain[j] =
|
|
(int) (((dev->sensor.gain_white_ref * 256) / average[j] -
|
|
1.0) / 0.4);
|
|
break;
|
|
case CCD_5345:
|
|
default:
|
|
/* 0.445 */
|
|
dev->frontend.gain[j] =
|
|
(int) (((dev->sensor.gain_white_ref * 256) / average[j] -
|
|
1.0) / 0.445);
|
|
break;
|
|
}
|
|
|
|
DBG (DBG_proc,
|
|
"gl646_coarse_gain_calibration: channel %d, average = %.2f, gain = %d\n",
|
|
j, average[j], dev->frontend.gain[j]);
|
|
}
|
|
|
|
if (dev->settings.scan_mode != 4) /* single pass color */
|
|
{
|
|
dev->frontend.gain[0] = dev->frontend.gain[1];
|
|
dev->frontend.gain[2] = dev->frontend.gain[1];
|
|
}
|
|
|
|
free (line);
|
|
DBG (DBG_proc, "gl646_coarse_gain_calibration: completed\n");
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* wait for lamp warmup by scanning the same line until difference
|
|
* between 2 scans is below a threshold
|
|
*/
|
|
static SANE_Status
|
|
gl646_init_regs_for_warmup (Genesys_Device * dev,
|
|
Genesys_Register_Set * local_reg,
|
|
int *channels, int *total_size)
|
|
{
|
|
int num_pixels = (int) (4 * 300);
|
|
int dpi=300, lincnt, exposure_time, words_per_line;
|
|
int startpixel, endpixel;
|
|
int steps = 0;
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
u_int16_t slope_table[256];
|
|
|
|
DBG (DBG_proc, "gl646_warmup_lamp\n");
|
|
|
|
memcpy (local_reg, dev->reg, (GENESYS_GL646_MAX_REGS + 1) * 2);
|
|
*total_size = num_pixels * 3 * 2 * 1; /* colors * bytes_per_color * scan lines */
|
|
|
|
/* ST12: 0x01 0x00 0x02 0x41 0x03 0x1f 0x04 0x53 0x05 0x10 0x06 0x10 0x08 0x02 0x09 0x00 0x0a 0x06 0x0b 0x04 */
|
|
/* ST24: 0x02 0x71 0x04 0x5f 0x05 0x50 0x08 0x0e 0x09 0x0c 0x0a 0x00 0x0b 0x0c */
|
|
#if 0
|
|
local_reg[reg_0x01].value = 0x00 /*0x02 */ ; /* disable shading, enable CCD, color, 1M */
|
|
local_reg[reg_0x02].value = 0x71 /*0x40 */ ; /* no forward/backward, no auto-home, motor off, full step */
|
|
local_reg[reg_0x03].value = 0x1f /*0x17 */ ; /* lamp on */
|
|
local_reg[reg_0x04].value = 0x53; /* 16 bits data, 16 bits A/D, color, Wolfson fe */
|
|
local_reg[reg_0x05].value = 0x50 /*0x00 */ ; /* CCD res = 600 dpi, 12 bits gamma, disable gamma, 24 clocks/pixel */
|
|
local_reg[reg_0x06].value = 0x10; /* power bit set, shading gain = 4 times system, no asic test */
|
|
local_reg[reg_0x07].value = 0x00; /* DMA access */
|
|
gl646_setup_sensor (dev, local_reg);
|
|
#endif
|
|
|
|
|
|
local_reg[reg_0x03].value |= REG03_LAMPPWR;
|
|
local_reg[reg_0x04].value =
|
|
(local_reg[reg_0x04].value & ~REG04_LINEART) | REG04_BITSET;
|
|
local_reg[reg_0x05].value = (local_reg[reg_0x05].value & ~REG05_GMMENB); /* disable gamma */
|
|
|
|
switch (dev->model->ccd_type)
|
|
{
|
|
case CCD_HP2300:
|
|
case CCD_HP2400:
|
|
local_reg[reg_0x01].value = REG01_DOGENB;
|
|
local_reg[reg_0x02].value = REG02_ACDCDIS | REG02_HALFSTEP;
|
|
local_reg[reg_0x04].value &= ~REG04_BITSET; /* disable 16 bits scanning */
|
|
local_reg[reg_0x05].value |= REG05_GMMENB; /* enable gamma */
|
|
*channels = 1;
|
|
steps = 1;
|
|
local_reg[reg_0x04].value =
|
|
(local_reg[reg_0x04].value & ~REG04_FILTER) | 0x08;
|
|
num_pixels = 2668;
|
|
if (dev->model->motor_type == MOTOR_HP2300)
|
|
{
|
|
dpi = 150;
|
|
slope_table[0] = 4480;
|
|
slope_table[1] = 4480;
|
|
}
|
|
else if (dev->model->motor_type == MOTOR_HP2400)
|
|
{
|
|
dpi = 200;
|
|
slope_table[0] = 7210;
|
|
slope_table[1] = 7210;
|
|
}
|
|
RIE (gl646_send_slope_table
|
|
(dev, 0, slope_table, local_reg[reg_0x21].value));
|
|
|
|
/* motor & movement control */
|
|
local_reg[reg_0x1e].value = 0x80;
|
|
local_reg[reg_0x1d].value |= REG1D_CKMANUAL;
|
|
local_reg[reg_0x1f].value = 0x10;
|
|
lincnt = 2;
|
|
local_reg[reg_0x21].value = 2; /* table one steps number for forward slope curve of the acc/dec */
|
|
local_reg[reg_0x22].value = 16; /* steps number of the forward steps for start/stop */
|
|
|
|
/* start pixel */
|
|
startpixel = dev->sensor.dummy_pixel + 34;
|
|
local_reg[reg_0x30].value = HIBYTE (startpixel);
|
|
local_reg[reg_0x31].value = LOBYTE (startpixel);
|
|
|
|
/* end CCD pixel */
|
|
endpixel = startpixel + num_pixels;
|
|
local_reg[reg_0x32].value = HIBYTE (endpixel);
|
|
local_reg[reg_0x33].value = LOBYTE (endpixel);
|
|
|
|
words_per_line = num_pixels / (dev->sensor.optical_res / dpi);
|
|
local_reg[reg_0x35].value = LOBYTE (HIWORD (words_per_line));
|
|
local_reg[reg_0x36].value = HIBYTE (LOWORD (words_per_line));
|
|
local_reg[reg_0x37].value = LOBYTE (LOWORD (words_per_line));
|
|
|
|
exposure_time = sanei_genesys_exposure_time (dev, local_reg, dpi),
|
|
local_reg[reg_0x38].value = HIBYTE (exposure_time);
|
|
local_reg[reg_0x39].value = LOBYTE (exposure_time);
|
|
|
|
/* monochrome scan */
|
|
*total_size = words_per_line * lincnt;
|
|
break;
|
|
default:
|
|
dpi = 300;
|
|
steps = 0;
|
|
*channels = 3;
|
|
num_pixels = 4 * 300;
|
|
local_reg[reg_0x1e].value = 0xf0 /*0x10 */ ; /* watch-dog time */
|
|
local_reg[reg_0x1f].value = 0x01; /* scan feed step for table one in two table mode only */
|
|
|
|
lincnt = 1;
|
|
|
|
/* motor & movement control */
|
|
local_reg[reg_0x21].value = 0x00; /* table one steps number for forward slope curve of the acc/dec */
|
|
local_reg[reg_0x22].value = 0x00; /* steps number of the forward steps for start/stop */
|
|
|
|
local_reg[reg_0x28].value = 0x01; /* PWM duty for lamp control */
|
|
local_reg[reg_0x29].value = 0xff;
|
|
|
|
local_reg[reg_0x2e].value = 0x88; /* set black&white threshold high level */
|
|
local_reg[reg_0x2f].value = 0x78; /* set black&white threshold low level */
|
|
|
|
local_reg[reg_0x38].value = 0x2a /*0x2b */ ; /* line period (exposure time) */
|
|
local_reg[reg_0x39].value = 0xf8 /*0x44 */ ;
|
|
local_reg[reg_0x35].value = 0x00 /*0x00 */ ; /* set maximum word size per line, for buffer full control (10800) */
|
|
local_reg[reg_0x36].value = 0x1d;
|
|
local_reg[reg_0x37].value = 0xe3;
|
|
break;
|
|
}
|
|
|
|
gl646_setup_sensor (dev, local_reg, 1, 1);
|
|
|
|
/* motor & movement control */
|
|
local_reg[reg_0x23].value = local_reg[reg_0x22].value; /* steps number of the backward steps for start/stop */
|
|
local_reg[reg_0x24].value = local_reg[reg_0x21].value; /* table one steps number backward slope curve of the acc/dec */
|
|
|
|
|
|
local_reg[reg_0x25].value = LOBYTE (HIWORD (lincnt)); /* scan line numbers - here one line */
|
|
local_reg[reg_0x26].value = HIBYTE (LOWORD (lincnt));
|
|
local_reg[reg_0x27].value = LOBYTE (LOWORD (lincnt));
|
|
|
|
local_reg[reg_0x2c].value = HIBYTE (dpi);
|
|
local_reg[reg_0x2d].value = LOBYTE (dpi);
|
|
|
|
local_reg[reg_0x34].value = dev->sensor.dummy_pixel;
|
|
|
|
/* set feed steps number of motor move */
|
|
local_reg[reg_0x3d].value = HIWORD (LOBYTE (steps));
|
|
local_reg[reg_0x3e].value = LOWORD (HIBYTE (steps));
|
|
local_reg[reg_0x3f].value = LOWORD (LOBYTE (steps));
|
|
|
|
/* ST12: 0x60 0x00 0x61 0x00 0x62 0x00 0x63 0x00 0x64 0x00 0x65 0x3f 0x66 0x00 0x67 0x00 0x68 0x51 0x69 0x20 */
|
|
/* ST24: 0x60 0x00 0x61 0x00 0x62 0x00 0x63 0x00 0x64 0x00 0x65 0x00 0x66 0x00 0x67 0x00 0x68 0x51 0x69 0x20 */
|
|
local_reg[reg_0x60].value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
|
|
local_reg[reg_0x61].value = 0x00; /* (21h+22h)/LPeriod */
|
|
local_reg[reg_0x62].value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
|
|
local_reg[reg_0x63].value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
|
|
local_reg[reg_0x64].value = 0x00; /* motor PWM frequency */
|
|
|
|
local_reg[reg_0x66].value = dev->gpo.enable[0] & 0x10;
|
|
local_reg[reg_0x67].value = dev->gpo.enable[1];
|
|
local_reg[reg_0x68].value = dev->gpo.enable[0];
|
|
local_reg[reg_0x69].value = dev->gpo.enable[1];
|
|
|
|
/* ST12: 0x6a 0x7f 0x6b 0xff 0x6c 0x00 0x6d 0x01 */
|
|
/* ST24: 0x6a 0x40 0x6b 0xff 0x6c 0x00 0x6d 0x01 */
|
|
switch (dev->model->motor_type)
|
|
{
|
|
default:
|
|
local_reg[reg_0x65].value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */
|
|
local_reg[reg_0x6a].value = 0x40; /* table two fast moving step type, PWM duty for table two */
|
|
local_reg[reg_0x6b].value = 0x01; /* table two steps number for acc/dec */
|
|
local_reg[reg_0x6c].value = 0x00; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
|
|
local_reg[reg_0x6d].value = 0x1f; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
|
|
break;
|
|
case MOTOR_HP2300:
|
|
case MOTOR_HP2400:
|
|
local_reg[reg_0x65].value = 0x3f;
|
|
local_reg[reg_0x6a].value = 0x7f;
|
|
local_reg[reg_0x6b].value = 0x02;
|
|
local_reg[reg_0x6d].value = 0x7f;
|
|
break;
|
|
}
|
|
|
|
RIE (gl646_set_fe (dev, AFE_INIT));
|
|
|
|
RIE (gl646_bulk_write_register
|
|
(dev, local_reg, GENESYS_GL646_MAX_REGS * 2));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* this function moves head without scanning, forward, then backward
|
|
* so that the head goes to park position.
|
|
* as a by-product, also check for lock
|
|
*/
|
|
static SANE_Status
|
|
gl646_repark_head (Genesys_Device * dev)
|
|
{
|
|
Genesys_Register_Set local_reg[GENESYS_GL646_MAX_REGS + 1];
|
|
u_int16_t slope_table[256];
|
|
unsigned int exposure_time; /* todo : modify sanei_genesys_exposure_time() */
|
|
SANE_Status status;
|
|
unsigned int steps = 232;
|
|
unsigned int lincnt = 212;
|
|
unsigned int dpi = 600;
|
|
unsigned int endpixel, expected;
|
|
|
|
DBG (DBG_proc, "gl646_repark_head\n");
|
|
|
|
memset (local_reg, 0, sizeof (local_reg));
|
|
memcpy (local_reg, dev->reg, (GENESYS_GL646_MAX_REGS + 1) * 2);
|
|
|
|
local_reg[reg_0x01].value =
|
|
local_reg[reg_0x01].value & ~REG01_DVDSET & ~REG01_FASTMOD & ~REG01_SCAN;
|
|
local_reg[reg_0x02].value =
|
|
(local_reg[reg_0x02].
|
|
value & ~REG02_FASTFED & ~REG02_STEPSEL) | REG02_HALFSTEP;
|
|
local_reg[reg_0x03].value =
|
|
(local_reg[reg_0x03].value & ~REG03_LAMPTIM) | 0x01;
|
|
|
|
gl646_setup_sensor (dev, local_reg, 0, 0);
|
|
|
|
local_reg[reg_0x1e].value = 0x10;
|
|
local_reg[reg_0x1f].value = 0x10;
|
|
local_reg[reg_0x20].value = 0x20;
|
|
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
gl646_setup_steps (dev, local_reg, dpi);
|
|
else
|
|
{
|
|
local_reg[reg_0x21].value = 4;
|
|
local_reg[reg_0x22].value = 16;
|
|
local_reg[reg_0x23].value = 16;
|
|
local_reg[reg_0x24].value = 4;
|
|
}
|
|
|
|
local_reg[reg_0x25].value = LOBYTE (HIWORD (lincnt));
|
|
local_reg[reg_0x26].value = HIBYTE (LOWORD (lincnt));
|
|
local_reg[reg_0x27].value = LOBYTE (LOWORD (lincnt));
|
|
|
|
local_reg[reg_0x2c].value = HIBYTE (dpi);
|
|
local_reg[reg_0x2d].value = LOBYTE (dpi);
|
|
|
|
/* start pixel */
|
|
local_reg[reg_0x30].value = HIBYTE (dev->sensor.dummy_pixel);
|
|
local_reg[reg_0x31].value = LOBYTE (dev->sensor.dummy_pixel);
|
|
|
|
|
|
/* end CCD pixel */
|
|
endpixel = dev->sensor.dummy_pixel + 2400;
|
|
local_reg[reg_0x32].value = HIBYTE (endpixel);
|
|
local_reg[reg_0x33].value = LOBYTE (endpixel);
|
|
|
|
local_reg[reg_0x34].value = dev->sensor.dummy_pixel;
|
|
|
|
local_reg[reg_0x35].value = LOBYTE (HIWORD (3600));
|
|
local_reg[reg_0x36].value = HIBYTE (LOWORD (3600)); /* */
|
|
local_reg[reg_0x37].value = LOBYTE (LOWORD (3600)); /* maximum word size per line=1200 */
|
|
|
|
local_reg[reg_0x3d].value = LOBYTE (HIWORD (steps));
|
|
local_reg[reg_0x3e].value = HIBYTE (LOWORD (steps));
|
|
local_reg[reg_0x3f].value = LOBYTE (LOWORD (steps));
|
|
|
|
exposure_time = 1600;
|
|
local_reg[reg_0x38].value = HIBYTE (exposure_time);
|
|
local_reg[reg_0x39].value = LOBYTE (exposure_time);
|
|
|
|
/* Z1MOD = Z2MOD = 0 */
|
|
local_reg[reg_0x60].value = LOBYTE (0);
|
|
local_reg[reg_0x61].value = HIBYTE (0);
|
|
local_reg[reg_0x62].value = LOBYTE (0);
|
|
local_reg[reg_0x63].value = HIBYTE (0);
|
|
|
|
local_reg[reg_0x65].value = 0x3f;
|
|
|
|
/* todo : maybe move thes in init once for all */
|
|
local_reg[reg_0x66].value = dev->gpo.enable[0] & 0x10;
|
|
local_reg[reg_0x67].value = dev->gpo.enable[1];
|
|
local_reg[reg_0x68].value = dev->gpo.enable[0];
|
|
local_reg[reg_0x69].value = dev->gpo.enable[1];
|
|
local_reg[reg_0x6a].value = 0x7f;
|
|
local_reg[reg_0x6b].value = 0x80;
|
|
local_reg[reg_0x6c].value = 0x00;
|
|
local_reg[reg_0x6d].value = 0x7f;
|
|
|
|
/* todo : move this into create_slope_table */
|
|
sanei_genesys_create_slope_table (dev,
|
|
slope_table,
|
|
local_reg[reg_0x21].value,
|
|
local_reg[reg_0x02].value & REG02_STEPSEL,
|
|
exposure_time, 0, 600);
|
|
|
|
sanei_genesys_create_slope_table (dev,
|
|
dev->slope_table1,
|
|
local_reg[reg_0x6b].value,
|
|
(local_reg[reg_0x6a].
|
|
value & REG6A_FSTPSEL) >> 6, 2700,
|
|
SANE_FALSE, dev->motor.base_ydpi / 4);
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 0, slope_table, local_reg[reg_0x21].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_repark_head: failed to send slope table 0: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_send_slope_table (dev, 1, dev->slope_table1,
|
|
local_reg[reg_0x6b].value);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_repark_head: failed to send slope table 1: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status =
|
|
gl646_bulk_write_register (dev, local_reg, GENESYS_GL646_MAX_REGS * 2);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_repark_head: failed to send registers: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
status = sanei_genesys_start_motor (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error, "gl646_repark_head: failed to start motor: %s\n",
|
|
sane_strstatus (status));
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
expected =
|
|
local_reg[reg_0x3d].value * 65536 + local_reg[reg_0x3e].value * 256 +
|
|
local_reg[reg_0x3f].value;
|
|
do
|
|
{
|
|
usleep (100 * 1000);
|
|
status = sanei_genesys_read_feed_steps (dev, &steps);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_repark_head: Failed to read feed steps: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
while (steps < expected);
|
|
|
|
/* toggle motor flag, put an huge step number and redo move backward */
|
|
status = gl646_park_head (dev, local_reg, 1);
|
|
DBG (DBG_proc, "gl646_repark_head: completed\n");
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* initialize ASIC : registers, motor tables, and gamma tables
|
|
* then ensure scanner's head is at home
|
|
*/
|
|
static SANE_Status
|
|
gl646_init (Genesys_Device * dev)
|
|
{
|
|
SANE_Status status;
|
|
struct timeval tv;
|
|
u_int8_t val;
|
|
int size;
|
|
u_int8_t data[64];
|
|
|
|
DBG_INIT ();
|
|
DBG (DBG_proc, "gl646_init\n");
|
|
|
|
/* Check if the device has already been initialized and powered up */
|
|
if (dev->already_initialized)
|
|
{
|
|
RIE (sanei_genesys_get_status (dev, &val));
|
|
if (val & REG41_PWRBIT)
|
|
{
|
|
DBG (DBG_info, "gl646_init: already initialized\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
|
|
dev->dark_average_data = NULL;
|
|
dev->white_average_data = NULL;
|
|
|
|
dev->settings.color_filter = 0;
|
|
gettimeofday (&tv, NULL);
|
|
dev->init_date = tv.tv_sec;
|
|
|
|
switch (dev->model->motor_type)
|
|
{
|
|
/* set to 11111 to spot bugs, sanei_genesys_exposure_time should
|
|
have obsoleted this field */
|
|
case MOTOR_5345:
|
|
dev->settings.exposure_time = 11111;
|
|
break;
|
|
|
|
case MOTOR_ST24:
|
|
dev->settings.exposure_time = 11000;
|
|
break;
|
|
default:
|
|
dev->settings.exposure_time = 11000;
|
|
break;
|
|
}
|
|
|
|
/* Set default values for registers */
|
|
gl646_init_regs (dev);
|
|
|
|
/* create slopes */
|
|
if (dev->model->motor_type == MOTOR_5345)
|
|
{
|
|
dev->reg[reg_0x02].value =
|
|
(dev->reg[reg_0x02].value & ~REG02_STEPSEL) | REG02_HALFSTEP;
|
|
sanei_genesys_create_slope_table (dev, dev->slope_table0,
|
|
dev->reg[reg_0x21].value,
|
|
dev->reg[reg_0x02].
|
|
value & REG02_STEPSEL,
|
|
sanei_genesys_exposure_time (dev,
|
|
dev->reg,
|
|
150), 0,
|
|
150);
|
|
sanei_genesys_create_slope_table (dev, dev->slope_table1,
|
|
dev->reg[reg_0x6b].value,
|
|
(dev->reg[reg_0x6a].
|
|
value & REG6A_FSTPSEL) >> 6, 3600, 0,
|
|
200);
|
|
}
|
|
else if (dev->model->motor_type == MOTOR_ST24)
|
|
{
|
|
sanei_genesys_create_slope_table (dev, dev->slope_table0,
|
|
dev->reg[reg_0x21].value,
|
|
dev->reg[reg_0x02].
|
|
value & REG02_STEPSEL,
|
|
dev->settings.exposure_time, 0, 600);
|
|
sanei_genesys_create_slope_table (dev, dev->slope_table1,
|
|
dev->reg[reg_0x6b].value,
|
|
(dev->reg[reg_0x6a].
|
|
value & REG6A_FSTPSEL) >> 6, 1200, 0,
|
|
400);
|
|
}
|
|
else
|
|
{
|
|
sanei_genesys_create_slope_table (dev, dev->slope_table0,
|
|
dev->reg[reg_0x21].value,
|
|
dev->reg[reg_0x02].
|
|
value & REG02_STEPSEL, 250, 0, 600);
|
|
sanei_genesys_create_slope_table (dev, dev->slope_table1,
|
|
dev->reg[reg_0x6b].value,
|
|
(dev->reg[reg_0x6a].
|
|
value & REG6A_FSTPSEL) >> 6, 250, 0,
|
|
600);
|
|
}
|
|
|
|
/* build default gamma tables */
|
|
if (dev->reg[reg_0x05].value & REG05_GMMTYPE)
|
|
size = 16384;
|
|
else
|
|
size = 4096;
|
|
|
|
if (dev->sensor.red_gamma_table == NULL)
|
|
{
|
|
dev->sensor.red_gamma_table = (u_int16_t *) malloc (2 * size);
|
|
if (dev->sensor.red_gamma_table == NULL)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init: could not allocate memory for gamma table\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
sanei_genesys_create_gamma_table (dev->sensor.red_gamma_table, size,
|
|
size - 1, size - 1,
|
|
dev->sensor.red_gamma);
|
|
}
|
|
if (dev->sensor.green_gamma_table == NULL)
|
|
{
|
|
dev->sensor.green_gamma_table = (u_int16_t *) malloc (2 * size);
|
|
if (dev->sensor.red_gamma_table == NULL)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init: could not allocate memory for gamma table\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
sanei_genesys_create_gamma_table (dev->sensor.green_gamma_table, size,
|
|
size - 1, size - 1,
|
|
dev->sensor.green_gamma);
|
|
}
|
|
if (dev->sensor.blue_gamma_table == NULL)
|
|
{
|
|
dev->sensor.blue_gamma_table = (u_int16_t *) malloc (2 * size);
|
|
if (dev->sensor.red_gamma_table == NULL)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init: could not allocate memory for gamma table\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
sanei_genesys_create_gamma_table (dev->sensor.blue_gamma_table, size,
|
|
size - 1, size - 1,
|
|
dev->sensor.blue_gamma);
|
|
}
|
|
|
|
/* Init device */
|
|
val = 0x04;
|
|
RIE (sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
|
|
VALUE_INIT, INDEX, 1, &val));
|
|
|
|
/* ASIC reset */
|
|
RIE (sanei_genesys_write_register (dev, 0x0e, 0x00));
|
|
usleep (10000UL); /* sleep 100 ms */
|
|
|
|
/* Write initial registers */
|
|
RIE (gl646_bulk_write_register (dev, dev->reg, GENESYS_GL646_MAX_REGS * 2));
|
|
|
|
/* Test ASIC and RAM */
|
|
if (!dev->model->flags & GENESYS_FLAG_LAZY_INIT)
|
|
{
|
|
RIE (gl646_asic_test (dev));
|
|
}
|
|
|
|
/* Set analog frontend */
|
|
RIE (gl646_set_fe (dev, AFE_INIT));
|
|
|
|
/* fix for timeouts at init */
|
|
if (dev->model->ccd_type == CCD_5345)
|
|
{
|
|
/* we read data without any pending scan to trigger timeout */
|
|
status = dev->model->cmd_set->bulk_read_data (dev, 0x45, data, 64);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
/* expected error */
|
|
DBG (DBG_error, "gl646_init: read stream reset ... %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
/* restore timeout */
|
|
}
|
|
|
|
/* Move home */
|
|
RIE (gl646_slow_back_home (dev, SANE_TRUE));
|
|
|
|
/* Init shading data */
|
|
RIE (sanei_genesys_init_shading_data (dev, dev->sensor.sensor_pixels));
|
|
|
|
/* ensure head is correctly parked, and check lock */
|
|
if (dev->model->flags & GENESYS_FLAG_REPARK)
|
|
{
|
|
status = gl646_repark_head (dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
if (status == SANE_STATUS_INVAL)
|
|
DBG (DBG_error0,
|
|
"Your scanner is locked. Please move the lock switch "
|
|
"to the unlocked position\n");
|
|
else
|
|
DBG (DBG_error, "gl646_init: gl646_repark_head failed: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* send gamma tables if needed */
|
|
status = gl646_send_gamma_table (dev, 1);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_error,
|
|
"gl646_init: failed to send generic gamma tables: %s\n",
|
|
sane_strstatus (status));
|
|
return status;
|
|
}
|
|
|
|
/* initial calibration reg values */
|
|
memcpy (dev->calib_reg, dev->reg, (GENESYS_GL646_MAX_REGS + 1) * 2);
|
|
|
|
/* Set powersaving (default = 15 minutes) */
|
|
RIE (gl646_set_powersaving (dev, 15));
|
|
dev->already_initialized = SANE_TRUE;
|
|
|
|
DBG (DBG_proc, "gl646_init: completed\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/** the gl646 command set */
|
|
static Genesys_Command_Set gl646_cmd_set = {
|
|
"gl646-generic", /* the name of this set */
|
|
|
|
gl646_init,
|
|
gl646_init_regs_for_warmup,
|
|
gl646_init_regs_for_coarse_calibration,
|
|
gl646_init_regs_for_shading,
|
|
gl646_init_regs_for_scan,
|
|
|
|
gl646_get_filter_bit,
|
|
gl646_get_lineart_bit,
|
|
gl646_get_bitset_bit,
|
|
gl646_get_gain4_bit,
|
|
gl646_get_fast_feed_bit,
|
|
gl646_test_buffer_empty_bit,
|
|
gl646_test_motor_flag_bit,
|
|
|
|
gl646_bulk_full_size,
|
|
|
|
gl646_set_fe,
|
|
gl646_set_powersaving,
|
|
gl646_save_power,
|
|
gl646_set_motor_power,
|
|
gl646_set_lamp_power,
|
|
|
|
gl646_begin_scan,
|
|
gl646_end_scan,
|
|
|
|
gl646_send_gamma_table,
|
|
|
|
gl646_search_start_position,
|
|
|
|
gl646_offset_calibration,
|
|
gl646_coarse_gain_calibration,
|
|
gl646_led_calibration,
|
|
|
|
gl646_slow_back_home,
|
|
gl646_park_head,
|
|
|
|
gl646_bulk_write_register,
|
|
gl646_bulk_write_data,
|
|
gl646_bulk_read_data,
|
|
};
|
|
|
|
SANE_Status
|
|
sanei_gl646_init_cmd_set (Genesys_Device * dev)
|
|
{
|
|
dev->model->cmd_set = &gl646_cmd_set;
|
|
return SANE_STATUS_GOOD;
|
|
}
|