summaryrefslogtreecommitdiff
path: root/backend/genesys_low.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/genesys_low.c')
-rw-r--r--backend/genesys_low.c1981
1 files changed, 1981 insertions, 0 deletions
diff --git a/backend/genesys_low.c b/backend/genesys_low.c
new file mode 100644
index 0000000..62563ae
--- /dev/null
+++ b/backend/genesys_low.c
@@ -0,0 +1,1981 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
+
+
+ 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.
+*/
+#undef BACKEND_NAME
+#define BACKEND_NAME genesys_low
+
+#include "genesys_low.h"
+
+/* ------------------------------------------------------------------------ */
+/* functions calling ASIC specific functions */
+/* ------------------------------------------------------------------------ */
+
+/**
+ * setup the hardware dependent functions
+ */
+SANE_Status
+sanei_genesys_init_cmd_set (Genesys_Device * dev)
+{
+ DBG_INIT ();
+ switch (dev->model->asic_type)
+ {
+ case GENESYS_GL646:
+ return sanei_gl646_init_cmd_set (dev);
+ case GENESYS_GL841:
+ return sanei_gl841_init_cmd_set (dev);
+ case GENESYS_GL843:
+ return sanei_gl843_init_cmd_set (dev);
+ case GENESYS_GL845: /* since only a few reg bits differs
+ we handle both together */
+ case GENESYS_GL846:
+ return sanei_gl846_init_cmd_set (dev);
+ case GENESYS_GL847:
+ return sanei_gl847_init_cmd_set (dev);
+ case GENESYS_GL124:
+ return sanei_gl124_init_cmd_set (dev);
+ default:
+ return SANE_STATUS_INVAL;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* General IO and debugging functions */
+/* ------------------------------------------------------------------------ */
+
+/* Write data to a pnm file (e.g. calibration). For debugging only */
+/* data is RGB or grey, with little endian byte order */
+SANE_Status
+sanei_genesys_write_pnm_file (char *filename, uint8_t * data, int depth,
+ int channels, int pixels_per_line, int lines)
+{
+ FILE *out;
+ int count;
+
+ DBG (DBG_info,
+ "sanei_genesys_write_pnm_file: depth=%d, channels=%d, ppl=%d, lines=%d\n",
+ depth, channels, pixels_per_line, lines);
+
+ out = fopen (filename, "w");
+ if (!out)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_pnm_file: could nor open %s for writing: %s\n",
+ filename, strerror (errno));
+ return SANE_STATUS_INVAL;
+ }
+ if(depth==1)
+ {
+ fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines);
+ }
+ else
+ {
+ fprintf (out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6',
+ pixels_per_line, lines, (int) pow (2, depth) - 1);
+ }
+ if (channels == 3)
+ {
+ for (count = 0; count < (pixels_per_line * lines * 3); count++)
+ {
+ if (depth == 16)
+ fputc (*(data + 1), out);
+ fputc (*(data++), out);
+ if (depth == 16)
+ data++;
+ }
+ }
+ else
+ {
+ if (depth==1)
+ {
+ pixels_per_line/=8;
+ }
+ for (count = 0; count < (pixels_per_line * lines); count++)
+ {
+ switch (depth)
+ {
+ case 8:
+ fputc (*(data + count), out);
+ break;
+ case 16:
+ fputc (*(data + 1), out);
+ fputc (*(data), out);
+ data += 2;
+ break;
+ default:
+ fputc(data[count], out);
+ break;
+ }
+ }
+ }
+ fclose (out);
+
+ DBG (DBG_proc, "sanei_genesys_write_pnm_file: finished\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* the following 2 functions are used to handle registers in a
+ way that doesn't depend on the actual ASIC type */
+
+/* Reads a register from a register set */
+SANE_Byte
+sanei_genesys_read_reg_from_set (Genesys_Register_Set * reg,
+ uint16_t address)
+{
+ SANE_Int i;
+
+ for (i = 0; i < GENESYS_MAX_REGS && reg[i].address; i++)
+ {
+ if (reg[i].address == address)
+ {
+ return reg[i].value;
+ }
+ }
+ return 0;
+}
+
+/* Reads a register from a register set */
+void
+sanei_genesys_set_reg_from_set (Genesys_Register_Set * reg, uint16_t address,
+ SANE_Byte value)
+{
+ SANE_Int i;
+
+ for (i = 0; i < GENESYS_MAX_REGS && reg[i].address; i++)
+ {
+ if (reg[i].address == address)
+ {
+ reg[i].value = value;
+ break;
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Read and write RAM, registers and AFE */
+/* ------------------------------------------------------------------------ */
+
+/** @brief write to one high (addr >= 0x100) register
+ * write to a register which address is higher than 0xff.
+ * @param dev opened device to write to
+ * @param reg LSB of register address
+ * @param val value to write
+ */
+SANE_Status
+sanei_genesys_write_hregister (Genesys_Device * dev, uint16_t reg, uint8_t val)
+{
+ SANE_Status status;
+ uint8_t buffer[2];
+
+ buffer[0]=reg & 0xff;
+ buffer[1]=val;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ 0x100 | VALUE_SET_REGISTER, INDEX, 2, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_genesys_write_hregister (0x%02x, 0x%02x): failed : %s\n", reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_write_hregister (0x%02x, 0x%02x) completed\n",
+ reg, val);
+
+ return status;
+}
+
+/** @brief read from one high (addr >= 0x100) register
+ * Read to a register which address is higher than 0xff. Second byte is check to detect
+ * physical link errors.
+ * @param dev opened device to read from
+ * @param reg LSB of register address
+ * @param val value to write
+ */
+SANE_Status
+sanei_genesys_read_hregister (Genesys_Device * dev, uint16_t reg, uint8_t * val)
+{
+ SANE_Status status;
+ SANE_Byte value[2];
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_BUFFER,
+ 0x100 | VALUE_GET_REGISTER, 0x22+((reg & 0xff)<<8), 2, value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_hregister (0x%02x): failed while reading register: %s\n",
+ reg, sane_strstatus (status));
+ return status;
+ }
+ *val=value[0];
+ DBG( DBG_io2, "sanei_genesys_read_hregister(0x%02x)=0x%02x\n",reg,*val);
+
+ /* check usb link status */
+ if((value[1] & 0xff) != 0x55)
+ {
+ DBG (DBG_error,"sanei_genesys_read_hregister: invalid read, scanner unplugged ?\n");
+ status=SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
+
+/**
+ * Write to one GL847 ASIC register
+URB 10 control 0x40 0x04 0x83 0x00 len 2 wrote 0xa6 0x04
+ */
+static SANE_Status
+sanei_genesys_write_gl847_register (Genesys_Device * dev, uint8_t reg, uint8_t val)
+{
+ SANE_Status status;
+ uint8_t buffer[2];
+
+ buffer[0]=reg;
+ buffer[1]=val;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_BUFFER,
+ VALUE_SET_REGISTER, INDEX, 2, buffer);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_genesys_write_gl847_register (0x%02x, 0x%02x): failed : %s\n", reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_write_gl847_register (0x%02x, 0x%02x) completed\n",
+ reg, val);
+
+ return status;
+}
+
+/**
+ * Write to one ASIC register
+ */
+SANE_Status
+sanei_genesys_write_register (Genesys_Device * dev, uint16_t reg, uint8_t val)
+{
+ SANE_Status status;
+ SANE_Byte reg8;
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ /* 16 bit register address space */
+ if(reg>255)
+ {
+ return sanei_genesys_write_hregister(dev, reg, val);
+ }
+
+ /* route to gl847 function if needed */
+ if(dev->model->asic_type==GENESYS_GL847
+ || dev->model->asic_type==GENESYS_GL845
+ || dev->model->asic_type==GENESYS_GL846
+ || dev->model->asic_type==GENESYS_GL124)
+ {
+ return sanei_genesys_write_gl847_register(dev, reg, val);
+ }
+
+ reg8=reg & 0xff;
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &reg8);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_register (0x%02x, 0x%02x): failed while setting register: %s\n",
+ reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_WRITE_REGISTER, INDEX, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_register (0x%02x, 0x%02x): failed while writing register value: %s\n",
+ reg, val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_write_register (0x%02x, 0x%02x) completed\n",
+ reg, val);
+
+ return status;
+}
+
+/**
+ * @brief write command to 0x8c endpoint
+ * Write a value to 0x8c end point (end access), for USB firmware related operations
+ * Known values are 0x0f, 0x11 for USB 2.0 data transfer and 0x0f,0x14 for USB1.1
+ * @param dev device to write to
+ * @param index index of the command
+ * @param val value to write
+ */
+SANE_Status
+sanei_genesys_write_0x8c (Genesys_Device * dev, uint8_t index, uint8_t val)
+{
+ SANE_Status status;
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ DBG (DBG_io, "sanei_genesys_write_0x8c: 0x%02x,0x%02x\n", index, val);
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_BUF_ENDACCESS, index, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_0x8c: failed %s\n", sane_strstatus (status));
+ }
+ return status;
+}
+
+/* read reg 0x41:
+ * URB 164 control 0xc0 0x04 0x8e 0x4122 len 2 read 0xfc 0x55
+ */
+static SANE_Status
+sanei_genesys_read_gl847_register (Genesys_Device * dev, uint16_t reg, uint8_t * val)
+{
+ SANE_Status status;
+ SANE_Byte value[2];
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_BUFFER,
+ VALUE_GET_REGISTER, 0x22+(reg<<8), 2, value);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_gl847_register (0x%02x): failed while setting register: %s\n",
+ reg, sane_strstatus (status));
+ return status;
+ }
+ *val=value[0];
+ DBG( DBG_io2, "sanei_genesys_read_gl847_register(0x%02x)=0x%02x\n",reg,*val);
+
+ /* check usb link status */
+ if((value[1] & 0xff) != 0x55)
+ {
+ DBG (DBG_error,"sanei_genesys_read_gl847_register: invalid read, scanner unplugged ?\n");
+ status=SANE_STATUS_IO_ERROR;
+ }
+ return status;
+}
+
+/* Read from one register */
+SANE_Status
+sanei_genesys_read_register (Genesys_Device * dev, uint16_t reg, uint8_t * val)
+{
+ SANE_Status status;
+ SANE_Byte reg8;
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ *val=0;
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ /* 16 bit register address space */
+ if(reg>255)
+ {
+ return sanei_genesys_read_hregister(dev, reg, val);
+ }
+
+ /* route to gl847 function if needed */
+ if(dev->model->asic_type==GENESYS_GL847
+ || dev->model->asic_type==GENESYS_GL845
+ || dev->model->asic_type==GENESYS_GL846
+ || dev->model->asic_type==GENESYS_GL124)
+ return sanei_genesys_read_gl847_register(dev, reg, val);
+
+ /* 8 bit register address space */
+ reg8=(SANE_Byte)(reg& 0Xff);
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_OUT, REQUEST_REGISTER,
+ VALUE_SET_REGISTER, INDEX, 1, &reg8);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_register (0x%02x, 0x%02x): failed while setting register: %s\n",
+ reg, *val, sane_strstatus (status));
+ return status;
+ }
+
+ *val = 0;
+
+ status =
+ sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_REGISTER,
+ VALUE_READ_REGISTER, INDEX, 1, val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_register (0x%02x, 0x%02x): failed while reading register value: %s\n",
+ reg, *val, sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_read_register (0x%02x, 0x%02x) completed\n",
+ reg, *val);
+
+ return status;
+}
+
+/* Set address for writing data */
+SANE_Status
+sanei_genesys_set_buffer_address (Genesys_Device * dev, uint32_t addr)
+{
+ SANE_Status status;
+
+ if(dev->model->asic_type==GENESYS_GL847
+ || dev->model->asic_type==GENESYS_GL845
+ || dev->model->asic_type==GENESYS_GL846
+ || dev->model->asic_type==GENESYS_GL124)
+ {
+ DBG (DBG_warn,
+ "sanei_genesys_set_buffer_address: shouldn't be used for GL846+ ASICs\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (DBG_io,
+ "sanei_genesys_set_buffer_address: setting address to 0x%05x\n",
+ addr & 0xfffffff0);
+
+ addr = addr >> 4;
+
+ status = sanei_genesys_write_register (dev, 0x2b, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_set_buffer_address: failed while writing low byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ addr = addr >> 8;
+ status = sanei_genesys_write_register (dev, 0x2a, (addr & 0xff));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_set_buffer_address: failed while writing high byte: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_set_buffer_address: completed\n");
+
+ return status;
+}
+
+/** read data for analog frontend
+ * @param dev device owning the AFE
+ * @param addr register address to read
+ * @param data placeholder for the result
+ * @return SANE_STATUS_GOOD is OK, else the error code
+ */
+SANE_Status
+sanei_genesys_fe_read_data (Genesys_Device * dev, uint8_t addr,
+ uint16_t *data)
+{
+ SANE_Status status;
+ uint8_t value;
+ Genesys_Register_Set reg[1];
+
+
+ DBG (DBG_proc, "sanei_genesys_fe_read_data: start\n");
+
+ reg[0].address = 0x50;
+ reg[0].value = addr;
+
+ /* set up read address */
+ status = dev->model->cmd_set->bulk_write_register (dev, reg, 1);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_fe_read_data: failed while bulk writing registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* read data */
+ RIE (sanei_genesys_read_register (dev, 0x46, &value));
+ *data=256*value;
+ RIE (sanei_genesys_read_register (dev, 0x47, &value));
+ *data+=value;
+
+ DBG (DBG_io, "sanei_genesys_fe_read_data (0x%02x, 0x%04x)\n", addr, *data);
+ DBG (DBG_proc, "sanei_genesys_fe_read_data: completed\n");
+
+ return status;
+}
+
+/* Write data for analog frontend */
+SANE_Status
+sanei_genesys_fe_write_data (Genesys_Device * dev, uint8_t addr,
+ uint16_t data)
+{
+ SANE_Status status;
+ Genesys_Register_Set reg[3];
+
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ DBG (DBG_io, "sanei_genesys_fe_write_data (0x%02x, 0x%04x)\n", addr, data);
+
+ reg[0].address = 0x51;
+ reg[0].value = addr;
+ reg[1].address = 0x3a;
+ reg[1].value = (data / 256) & 0xff;
+ reg[2].address = 0x3b;
+ reg[2].value = data & 0xff;
+ if (dev->model->asic_type == GENESYS_GL124)
+ {
+ reg[1].address = 0x5d;
+ reg[2].address = 0x5e;
+ }
+
+ status = dev->model->cmd_set->bulk_write_register (dev, reg, 3);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_fe_write_data: failed while bulk writing registers: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_io, "sanei_genesys_fe_write_data: completed\n");
+
+ return status;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Medium level functions */
+/* ------------------------------------------------------------------------ */
+
+/** read the status register
+ */
+SANE_Status
+sanei_genesys_get_status (Genesys_Device * dev, uint8_t * status)
+{
+#ifdef UNIT_TESTING
+ if(dev->usb_mode<0)
+ {
+ *status=0;
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ if(dev->model->asic_type==GENESYS_GL124)
+ return sanei_genesys_read_hregister(dev, 0x101, status);
+ return sanei_genesys_read_register (dev, 0x41, status);
+}
+
+/**
+ * decodes and prints content of status register
+ * @param val value read from status register
+ */
+void sanei_genesys_print_status (uint8_t val)
+{
+ char msg[80];
+
+ sprintf (msg, "%s%s%s%s%s%s%s%s",
+ val & PWRBIT ? "PWRBIT " : "",
+ val & BUFEMPTY ? "BUFEMPTY " : "",
+ val & FEEDFSH ? "FEEDFSH " : "",
+ val & SCANFSH ? "SCANFSH " : "",
+ val & HOMESNR ? "HOMESNR " : "",
+ val & LAMPSTS ? "LAMPSTS " : "",
+ val & FEBUSY ? "FEBUSY " : "",
+ val & MOTORENB ? "MOTORENB" : "");
+ DBG (DBG_info, "status=%s\n", msg);
+}
+
+#if 0
+/* returns pixels per line from register set */
+/*candidate for moving into chip specific files?*/
+static int
+genesys_pixels_per_line (Genesys_Register_Set * reg)
+{
+ int pixels_per_line;
+
+ pixels_per_line =
+ sanei_genesys_read_reg_from_set (reg,
+ 0x32) * 256 +
+ sanei_genesys_read_reg_from_set (reg, 0x33);
+ pixels_per_line -=
+ (sanei_genesys_read_reg_from_set (reg, 0x30) * 256 +
+ sanei_genesys_read_reg_from_set (reg, 0x31));
+
+ return pixels_per_line;
+}
+
+/* returns dpiset from register set */
+/*candidate for moving into chip specific files?*/
+static int
+genesys_dpiset (Genesys_Register_Set * reg)
+{
+ int dpiset;
+
+ dpiset =
+ sanei_genesys_read_reg_from_set (reg,
+ 0x2c) * 256 +
+ sanei_genesys_read_reg_from_set (reg, 0x2d);
+
+ return dpiset;
+}
+#endif
+
+/** read the number of valid words in scanner's RAM
+ * ie registers 42-43-44
+ */
+/*candidate for moving into chip specific files?*/
+SANE_Status
+sanei_genesys_read_valid_words (Genesys_Device * dev, unsigned int *words)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBGSTART;
+ switch (dev->model->asic_type)
+ {
+ case GENESYS_GL124:
+ RIE (sanei_genesys_read_hregister (dev, 0x102, &value));
+ *words = (value & 0x03);
+ RIE (sanei_genesys_read_hregister (dev, 0x103, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_hregister (dev, 0x104, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_hregister (dev, 0x105, &value));
+ *words = *words * 256 + value;
+ break;
+
+ case GENESYS_GL845:
+ case GENESYS_GL846:
+ RIE (sanei_genesys_read_register (dev, 0x42, &value));
+ *words = (value & 0x02);
+ RIE (sanei_genesys_read_register (dev, 0x43, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x44, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x45, &value));
+ *words = *words * 256 + value;
+ break;
+
+ case GENESYS_GL847:
+ RIE (sanei_genesys_read_register (dev, 0x42, &value));
+ *words = (value & 0x03);
+ RIE (sanei_genesys_read_register (dev, 0x43, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x44, &value));
+ *words = *words * 256 + value;
+ RIE (sanei_genesys_read_register (dev, 0x45, &value));
+ *words = *words * 256 + value;
+ break;
+
+ default:
+ RIE (sanei_genesys_read_register (dev, 0x44, &value));
+ *words = value;
+ RIE (sanei_genesys_read_register (dev, 0x43, &value));
+ *words += (value * 256);
+ RIE (sanei_genesys_read_register (dev, 0x42, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *words += ((value & 0x03) * 256 * 256);
+ else
+ *words += ((value & 0x0f) * 256 * 256);
+ }
+
+ DBG (DBG_proc, "%s: %d words\n", __FUNCTION__, *words);
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+/** read the number of lines scanned
+ * ie registers 4b-4c-4d
+ */
+SANE_Status
+sanei_genesys_read_scancnt (Genesys_Device * dev, unsigned int *words)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBG (DBG_proc, "sanei_genesys_read_scancnt: start\n");
+
+ if (dev->model->asic_type == GENESYS_GL124)
+ {
+ RIE (sanei_genesys_read_hregister (dev, 0x10b, &value));
+ *words = (value & 0x0f) << 16;
+ RIE (sanei_genesys_read_hregister (dev, 0x10c, &value));
+ *words += (value << 8);
+ RIE (sanei_genesys_read_hregister (dev, 0x10d, &value));
+ *words += value;
+ }
+ else
+ {
+ RIE (sanei_genesys_read_register (dev, 0x4d, &value));
+ *words = value;
+ RIE (sanei_genesys_read_register (dev, 0x4c, &value));
+ *words += (value * 256);
+ RIE (sanei_genesys_read_register (dev, 0x4b, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *words += ((value & 0x03) * 256 * 256);
+ else
+ *words += ((value & 0x0f) * 256 * 256);
+ }
+
+ DBG (DBG_proc, "sanei_genesys_read_scancnt: %d lines\n", *words);
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * Find register in set
+ * @param regs register set to search
+ * @param addr addres of the searched register
+ * @return a Genesys_Register_Set pointer corresponding to the required
+ * address in ASIC space. Or NULL if not found.
+ */
+Genesys_Register_Set *
+sanei_genesys_get_address (Genesys_Register_Set * regs, uint16_t addr)
+{
+ int i;
+ for (i = 0; i < GENESYS_MAX_REGS; i++)
+ {
+ if (regs[i].address == addr)
+ return &regs[i];
+ }
+ DBG (DBG_error, "sanei_genesys_get_address: failed to find address for register 0x%02x, crash expected !\n",addr);
+ return NULL;
+}
+
+/**
+ * set a 16 bit value in the given register set.
+ * @param regs register set where to set values
+ * @param addr address of the first register index to set
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_set_double(Genesys_Register_Set *regs, uint16_t addr, uint16_t value)
+{
+ Genesys_Register_Set *r;
+
+ /* high byte */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = HIBYTE (value);
+
+ /* low byte */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = LOBYTE (value);
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * set a 24 bit value in the given register set.
+ * @param regs register set where to set values
+ * @param addr address of the first register index to set
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_set_triple(Genesys_Register_Set *regs, uint16_t addr, uint32_t value)
+{
+ Genesys_Register_Set *r;
+
+ /* low byte of highword */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = LOBYTE (HIWORD(value));
+
+ /* high byte of low word */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = HIBYTE (LOWORD(value));
+
+ /* low byte of low word */
+ r = sanei_genesys_get_address (regs, addr+2);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ r->value = LOBYTE (LOWORD(value));
+
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * get a 16 bit value in the given register set.
+ * @param regs register set where to read values
+ * @param addr address of the first register index to read
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_get_double(Genesys_Register_Set *regs, uint16_t addr, uint16_t *value)
+{
+ Genesys_Register_Set *r;
+ uint16_t result=0;
+
+ /* high byte */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result=r->value<<8;
+
+ /* low byte */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result+=r->value;
+
+ *value=result;
+ return SANE_STATUS_GOOD;
+}
+
+/**
+ * get a 24 bit value in the given register set.
+ * @param regs register set where to read values
+ * @param addr address of the first register index to read
+ * @param value value to set
+ * @return SANE_STATUS_INVAL if the index doesn't exist in register set
+ */
+SANE_Status
+sanei_genesys_get_triple(Genesys_Register_Set *regs, uint16_t addr, uint32_t *value)
+{
+ Genesys_Register_Set *r;
+ uint32_t result=0;
+
+ /* low byte of highword */
+ r = sanei_genesys_get_address (regs, addr);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result=r->value<<16;
+
+ /* high byte of low word */
+ r = sanei_genesys_get_address (regs, addr+1);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result+=(r->value<<8);
+
+ /* low byte of low word */
+ r = sanei_genesys_get_address (regs, addr+2);
+ if(r==NULL)
+ {
+ return SANE_STATUS_INVAL;
+ }
+ result+=r->value;
+
+ *value=result;
+ return SANE_STATUS_GOOD;
+}
+
+/* Checks if the scan buffer is empty */
+SANE_Status
+sanei_genesys_test_buffer_empty (Genesys_Device * dev, SANE_Bool * empty)
+{
+ uint8_t val = 0;
+ SANE_Status status;
+
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_test_buffer_empty: failed to read buffer status: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ if (dev->model->cmd_set->test_buffer_empty_bit (val))
+ {
+ DBG (DBG_io2, "sanei_genesys_test_buffer_empty: buffer is empty\n");
+ *empty = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+ }
+
+ *empty = SANE_FALSE;
+
+ DBG (DBG_io, "sanei_genesys_test_buffer_empty: buffer is filled\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Read data (e.g scanned image) from scan buffer */
+SANE_Status
+sanei_genesys_read_data_from_scanner (Genesys_Device * dev, uint8_t * data,
+ size_t size)
+{
+ SANE_Status status;
+ int time_count = 0;
+ unsigned int words = 0;
+
+ DBG (DBG_proc, "sanei_genesys_read_data_from_scanner (size = %lu bytes)\n",
+ (u_long) size);
+
+ if (size & 1)
+ DBG (DBG_info,
+ "WARNING sanei_genesys_read_data_from_scanner: odd number of bytes\n");
+
+ /* wait until buffer not empty for up to 5 seconds */
+ do
+ {
+ status = sanei_genesys_read_valid_words (dev, &words);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_data_from_scanner: checking for empty buffer failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ if (words == 0)
+ {
+ usleep (10000); /* wait 10 msec */
+ time_count++;
+ }
+ }
+ while ((time_count < 2500*2) && (words == 0));
+
+ if (words == 0) /* timeout, buffer does not get filled */
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_data_from_scanner: timeout, buffer does not get filled\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ status = dev->model->cmd_set->bulk_read_data (dev, 0x45, data, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_read_data_from_scanner: reading bulk data failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DBG (DBG_proc, "sanei_genesys_read_data_from_scanner: completed\n");
+ return SANE_STATUS_GOOD;
+}
+SANE_Status
+sanei_genesys_read_feed_steps (Genesys_Device * dev, unsigned int *steps)
+{
+ SANE_Status status;
+ uint8_t value;
+
+ DBG (DBG_proc, "sanei_genesys_read_feed_steps\n");
+
+ if (dev->model->asic_type == GENESYS_GL124)
+ {
+ RIE (sanei_genesys_read_hregister (dev, 0x108, &value));
+ *steps = (value & 0x1f) << 16;
+ RIE (sanei_genesys_read_hregister (dev, 0x109, &value));
+ *steps += (value << 8);
+ RIE (sanei_genesys_read_hregister (dev, 0x10a, &value));
+ *steps += value;
+ }
+ else
+ {
+ RIE (sanei_genesys_read_register (dev, 0x4a, &value));
+ *steps = value;
+ RIE (sanei_genesys_read_register (dev, 0x49, &value));
+ *steps += (value * 256);
+ RIE (sanei_genesys_read_register (dev, 0x48, &value));
+ if (dev->model->asic_type == GENESYS_GL646)
+ *steps += ((value & 0x03) * 256 * 256);
+ else if (dev->model->asic_type == GENESYS_GL841)
+ *steps += ((value & 0x0f) * 256 * 256);
+ else
+ *steps += ((value & 0x1f) * 256 * 256);
+ }
+
+ DBG (DBG_proc, "sanei_genesys_read_feed_steps: %d steps\n", *steps);
+ return SANE_STATUS_GOOD;
+}
+
+
+/**
+ * Write to many registers at once
+ * Note: sequential call to write register, no effective
+ * bulk write implemented.
+ * @param dev device to write to
+ * @param reg pointer to an array of registers
+ * @param elems size of the array
+ */
+SANE_Status
+sanei_genesys_bulk_write_register (Genesys_Device * dev,
+ Genesys_Register_Set * reg,
+ size_t elems)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t i;
+
+ for (i = 0; i < elems && status == SANE_STATUS_GOOD; i++)
+ {
+ if (reg[i].address != 0)
+ {
+ status =
+ sanei_genesys_write_register (dev, reg[i].address, reg[i].value);
+ }
+ }
+
+ DBG (DBG_io, "%s: wrote %lu registers\n", __FUNCTION__, (u_long) elems);
+ return status;
+}
+
+
+
+/**
+ * writes a block of data to AHB
+ * @param dn USB device index
+ * @param usb_mode usb mode : -1, fake usb, 1 usb 1.1, 2 usb 2.0
+ * @param addr AHB address to write to
+ * @param size size of the chunk of data
+ * @param data pointer to the data to write
+ */
+SANE_Status
+sanei_genesys_write_ahb (SANE_Int dn, int usb_mode, uint32_t addr, uint32_t size, uint8_t * data)
+{
+ uint8_t outdata[8];
+ size_t written,blksize;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int i;
+ char msg[100]="AHB=";
+
+ outdata[0] = addr & 0xff;
+ outdata[1] = ((addr >> 8) & 0xff);
+ outdata[2] = ((addr >> 16) & 0xff);
+ outdata[3] = ((addr >> 24) & 0xff);
+ outdata[4] = (size & 0xff);
+ outdata[5] = ((size >> 8) & 0xff);
+ outdata[6] = ((size >> 16) & 0xff);
+ outdata[7] = ((size >> 24) & 0xff);
+
+ if (DBG_LEVEL >= DBG_io)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ sprintf (msg+strlen(msg), " 0x%02x", outdata[i]);
+ }
+ DBG (DBG_io, "%s: write(0x%08x,0x%08x)\n", __FUNCTION__, addr,size);
+ DBG (DBG_io, "%s: %s\n", __FUNCTION__, msg);
+ }
+
+ /* no effective write if fake USB */
+ if(usb_mode<0)
+ {
+ DBGCOMPLETED;
+ return status;
+ }
+
+ /* write addr and size for AHB */
+ status =
+ sanei_usb_control_msg (dn, REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER,
+ 0x01, 8, outdata);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "sanei_genesys_write_ahb: failed while setting addr and size: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* write actual data */
+ written = 0;
+ do
+ {
+ if (size - written > BULKOUT_MAXSIZE)
+ {
+ blksize = BULKOUT_MAXSIZE;
+ }
+ else
+ {
+ blksize = size - written;
+ }
+ status = sanei_usb_write_bulk (dn, data + written, &blksize);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_write_ahb: failed while writing bulk data: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ written += blksize;
+ }
+ while (written < size);
+
+ return status;
+}
+
+/** @brief generates gamma buffer to transfer
+ * Generates gamma table buffer to send to ASIC. Applies
+ * contrast and brightness if set.
+ * @param dev device to set up
+ * @param bits number of bits used by gamma
+ * @param max value for gamma
+ * @param size of the gamma table
+ * @param gamma allocated gamma buffer to fill
+ * @returns SANE_STATUS_GOOD or SANE_STATUS_NO_MEM
+ */
+SANE_Status sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
+ int bits,
+ int max,
+ int size,
+ uint8_t *gamma)
+{
+ int i;
+ uint16_t value, *lut=NULL;
+
+ if(dev->settings.contrast!=0 || dev->settings.brightness!=0)
+ {
+ lut=(uint16_t *)malloc(65536*2);
+ if(lut==NULL)
+ {
+ free(gamma);
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_genesys_load_lut((unsigned char *)lut,
+ bits,
+ bits,
+ 0,
+ max,
+ dev->settings.contrast,
+ dev->settings.brightness);
+ for (i = 0; i < size-1; i++)
+ {
+ value=dev->sensor.gamma_table[GENESYS_RED][i];
+ value=lut[value];
+ gamma[i * 2 + size * 0 + 0] = value & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_GREEN][i];
+ value=lut[value];
+ gamma[i * 2 + size * 2 + 0] = value & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_BLUE][i];
+ value=lut[value];
+ gamma[i * 2 + size * 4 + 0] = value & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
+ }
+ }
+ else
+ {
+ for (i = 0; i < size-1; i++)
+ {
+ value=dev->sensor.gamma_table[GENESYS_RED][i];
+ gamma[i * 2 + size * 0 + 0] = value & 0xff;
+ gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_GREEN][i];
+ gamma[i * 2 + size * 2 + 0] = value & 0xff;
+ gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
+
+ value=dev->sensor.gamma_table[GENESYS_BLUE][i];
+ gamma[i * 2 + size * 4 + 0] = value & 0xff;
+ gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
+ }
+ }
+
+
+ if(lut!=NULL)
+ {
+ free(lut);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief send gamma table to scanner
+ * This function sends generic gamma table (ie ones built with
+ * provided gamma) or the user defined one if provided by
+ * fontend. Used by gl846+ ASICs
+ * @param dev device to write to
+ */
+SANE_Status
+sanei_genesys_send_gamma_table (Genesys_Device * dev)
+{
+ int size;
+ int i;
+ uint8_t *gamma, val;
+ SANE_Status status;
+
+ DBGSTART;
+
+ size = 256 + 1;
+
+ /* allocate temporary gamma tables: 16 bits words, 3 channels */
+ gamma = (uint8_t *) malloc (size * 2 * 3);
+ if (!gamma)
+ {
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(gamma, 255, size*3*2);
+
+ RIE(sanei_genesys_generate_gamma_buffer(dev, 16, 65535, size, gamma));
+
+ /* loop sending gamma tables NOTE: 0x01000000 not 0x10000000 */
+ for (i = 0; i < 3; i++)
+ {
+ /* clear corresponding GMM_N bit */
+ RIEF (sanei_genesys_read_register (dev, 0xbd, &val), gamma);
+ val &= ~(0x01 << i);
+ RIEF (sanei_genesys_write_register (dev, 0xbd, val), gamma);
+
+ /* clear corresponding GMM_F bit */
+ RIEF (sanei_genesys_read_register (dev, 0xbe, &val), gamma);
+ val &= ~(0x01 << i);
+ RIEF (sanei_genesys_write_register (dev, 0xbe, val), gamma);
+
+ /* set GMM_Z */
+ RIEF (sanei_genesys_write_register (dev, 0xc5+2*i, gamma[size*2*i+1]), gamma);
+ RIEF (sanei_genesys_write_register (dev, 0xc6+2*i, gamma[size*2*i]), gamma);
+
+ status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, 0x01000000 + 0x200 * i, (size-1) * 2, gamma + i * size * 2+2);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (gamma);
+ DBG (DBG_error,
+ "%s: write to AHB failed writing table %d (%s)\n", __FUNCTION__,
+ i, sane_strstatus (status));
+ }
+ }
+
+ free (gamma);
+ DBGCOMPLETED;
+ return status;
+}
+
+/** @brief initialize device
+ * initialize backend and ASIC : registers, motor tables, and gamma tables
+ * then ensure scanner's head is at home. Designed for gl846+ ASICs
+ * @param dev device to initialize
+ * @param max_regs umber of maximum used registers
+ */
+SANE_Status
+sanei_genesys_asic_init (Genesys_Device * dev, int max_regs)
+{
+ SANE_Status status;
+ uint8_t val;
+ SANE_Bool cold = SANE_TRUE;
+ int size, i;
+
+ DBGSTART;
+
+ /* URB 16 control 0xc0 0x0c 0x8e 0x0b len 1 read 0x00 */
+ if(dev->usb_mode>=0)
+ {
+ status = sanei_usb_control_msg (dev->dn, REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_GET_REGISTER, 0x00, 1, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "%s: request register failed %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_io2, "%s: value=0x%02x\n", __FUNCTION__, val);
+ DBG (DBG_info, "%s: device is %s\n", __FUNCTION__, (val & 0x08) ? "USB 1.0" : "USB2.0");
+ if (val & 0x08)
+ {
+ dev->usb_mode = 1;
+ }
+ else
+ {
+ dev->usb_mode = 2;
+ }
+ }
+
+ /* check if the device has already been initialized and powered up
+ * we read register 6 and check PWRBIT, if reset scanner has been
+ * freshly powered up. This bit will be set to later so that following
+ * reads can detect power down/up cycle*/
+ RIE (sanei_genesys_read_register (dev, 0x06, &val));
+ /* test for POWER bit */
+ if (val & 0x10)
+ {
+ cold = SANE_FALSE;
+ }
+ DBG (DBG_info, "%s: device is %s\n", __FUNCTION__, cold ? "cold" : "warm");
+
+ /* don't do anything if backend is initialized and hardware hasn't been
+ * replug */
+ if (dev->already_initialized && !cold)
+ {
+ DBG (DBG_info, "%s: already initialized, nothing to do\n", __FUNCTION__);
+ return SANE_STATUS_GOOD;
+ }
+
+ /* set up hardware and registers */
+ RIE (dev->model->cmd_set->asic_boot (dev, cold));
+
+ /* now hardware part is OK, set up device struct */
+ FREE_IFNOT_NULL (dev->white_average_data);
+ FREE_IFNOT_NULL (dev->dark_average_data);
+ FREE_IFNOT_NULL (dev->sensor.gamma_table[0]);
+ FREE_IFNOT_NULL (dev->sensor.gamma_table[1]);
+ FREE_IFNOT_NULL (dev->sensor.gamma_table[2]);
+
+ dev->settings.color_filter = 0;
+
+ /* duplicate initial values into calibration registers */
+ memcpy (dev->calib_reg, dev->reg, max_regs * sizeof (Genesys_Register_Set));
+
+ /* Set analog frontend */
+ RIE (dev->model->cmd_set->set_fe (dev, AFE_INIT));
+
+ /* init gamma tables */
+ size = 256;
+
+ for(i=0;i<3;i++)
+ {
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ dev->sensor.gamma_table[i] = (uint16_t *) malloc (2 * size);
+ if (dev->sensor.gamma_table[i] == NULL)
+ {
+ DBG (DBG_error, "%s: could not allocate memory for gamma table %d\n",
+ __FUNCTION__, i);
+ return SANE_STATUS_NO_MEM;
+ }
+ sanei_genesys_create_gamma_table (dev->sensor.gamma_table[i],
+ size,
+ 65535,
+ 65535,
+ dev->sensor.gamma[i]);
+ }
+ }
+
+ dev->oe_buffer.buffer = NULL;
+ dev->already_initialized = SANE_TRUE;
+
+ /* Move to home if needed */
+ RIE (dev->model->cmd_set->slow_back_home (dev, SANE_TRUE));
+ dev->scanhead_position_in_steps = 0;
+
+ /* Set powersaving (default = 15 minutes) */
+ RIE (dev->model->cmd_set->set_powersaving (dev, 15));
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**
+ * Wait for the scanning head to park
+ */
+SANE_Status
+sanei_genesys_wait_for_home (Genesys_Device * dev)
+{
+ SANE_Status status;
+ uint8_t val;
+ int loop;
+
+ DBGSTART;
+
+ /* clear the parking status whatever the outcome of the function */
+ dev->parking=SANE_FALSE;
+
+ /* read initial status, if head isn't at home and motor is on
+ * we are parking, so we wait.
+ * gl847/gl124 need 2 reads for reliable results */
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ usleep (10000);
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+
+ /* if at home, return */
+ if(val & HOMESNR)
+ {
+ DBG (DBG_info,
+ "%s: already at home\n", __FUNCTION__);
+ return status;
+ }
+
+ /* loop for 30 s max, polling home sensor */
+ loop = 0;
+ do
+ {
+ /* wait 100 ms */
+ usleep (100000);
+ status = sanei_genesys_get_status (dev, &val);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "%s: failed to read home sensor: %s\n", __FUNCTION__,
+ sane_strstatus (status));
+ return status;
+ }
+ if (DBG_LEVEL >= DBG_io2)
+ {
+ sanei_genesys_print_status (val);
+ }
+ ++loop;
+ }
+ while (loop < 300 && !(val & HOMESNR) && status == SANE_STATUS_GOOD);
+
+ DBGCOMPLETED;
+ return status;
+}
+
+/**@brief compute hardware sensor dpi to use
+ * compute the sensor hardware dpi based on target resolution.
+ * A lower dpihw enable faster scans.
+ * @param dev device used for the scan
+ * @param xres x resolution of the scan
+ * @return the hardware dpi to use
+ */
+int sanei_genesys_compute_dpihw(Genesys_Device *dev, int xres)
+{
+ /* some scanners use alxways hardware dpi for sensor */
+ if (dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE)
+ {
+ return dev->sensor.optical_res;
+ }
+
+ /* can't be below 600 dpi */
+ if (xres <= 600)
+ {
+ return 600;
+ }
+ if (xres <= dev->sensor.optical_res / 4)
+ {
+ return dev->sensor.optical_res / 4;
+ }
+ if (xres <= dev->sensor.optical_res / 2)
+ {
+ return dev->sensor.optical_res / 2;
+ }
+ return dev->sensor.optical_res;
+}
+
+/** @brief motor profile
+ * search for the database of motor profiles and get the best one. Each
+ * profile is at full step and at a reference exposure. Use first entry
+ * by default.
+ * @param motors motor profile database
+ * @param motor_type motor id
+ * @param exposure exposure time
+ * @return a pointer to a Motor_Profile struct
+ */
+Motor_Profile *sanei_genesys_get_motor_profile(Motor_Profile *motors, int motor_type, int exposure)
+{
+ unsigned int i;
+ int idx;
+
+ i=0;
+ idx=-1;
+ while(motors[i].exposure!=0)
+ {
+ /* exact match */
+ if(motors[i].motor_type==motor_type && motors[i].exposure==exposure)
+ {
+ return &(motors[i]);
+ }
+
+ /* closest match */
+ if(motors[i].motor_type==motor_type)
+ {
+ /* if profile exposure is higher than the required one,
+ * the entry is a candidate for the closest match */
+ if(motors[i].exposure>=exposure)
+ {
+ if(idx<0)
+ {
+ /* no match found yet */
+ idx=i;
+ }
+ else
+ {
+ /* test for better match */
+ if(motors[i].exposure<motors[idx].exposure)
+ {
+ idx=i;
+ }
+ }
+ }
+ }
+ i++;
+ }
+
+ /* default fallback */
+ if(idx<0)
+ {
+ DBG (DBG_warn,"%s: using default motor profile\n",__FUNCTION__);
+ idx=0;
+ }
+
+ return &(motors[idx]);
+}
+
+/**@brief compute motor step type to use
+ * compute the step type (full, half, quarter, ...) to use based
+ * on target resolution
+ * @param motors motor profile database
+ * @param motor_type motor id
+ * @param exposure sensor exposure
+ * @return 0 for full step
+ * 1 for half step
+ * 2 for quarter step
+ * 3 for eighth step
+ */
+int sanei_genesys_compute_step_type(Motor_Profile *motors,
+ int motor_type,
+ int exposure)
+{
+Motor_Profile *profile;
+
+ profile=sanei_genesys_get_motor_profile(motors, motor_type, exposure);
+ return profile->step_type;
+}
+
+/** @brief generate slope table
+ * Generate the slope table to use for the scan using a reference slope
+ * table.
+ * @param slope pointer to the slope table to fill
+ * @param steps pointer to return used step number
+ * @param dpi desired motor resolution
+ * @param exposure exposure used
+ * @param base_dpi base resolution of the motor
+ * @param step_type step type used for scan
+ * @param factor shrink factor for the slope
+ * @param motor_type motor id
+ * @param motors motor profile database
+ */
+int sanei_genesys_slope_table(uint16_t *slope,
+ int *steps,
+ int dpi,
+ int exposure,
+ int base_dpi,
+ int step_type,
+ int factor,
+ int motor_type,
+ Motor_Profile *motors)
+{
+int sum, i;
+uint16_t target,current;
+Motor_Profile *profile;
+
+ /* required speed */
+ target=((exposure * dpi) / base_dpi)>>step_type;
+ DBG (DBG_io2, "%s: target=%d\n", __FUNCTION__, target);
+
+ /* fill result with target speed */
+ for(i=0;i<SLOPE_TABLE_SIZE;i++)
+ slope[i]=target;
+
+ profile=sanei_genesys_get_motor_profile(motors, motor_type, exposure);
+
+ /* use profile to build table */
+ i=0;
+ sum=0;
+
+ /* first step is used unmodified */
+ current=profile->table[0];
+
+ /* loop on profile copying and apply step type */
+ while(profile->table[i]!=0 && current>=target)
+ {
+ slope[i]=current;
+ sum+=slope[i];
+ i++;
+ current=profile->table[i]>>step_type;
+ }
+
+ /* range checking */
+ if(profile->table[i]==0 && DBG_LEVEL >= DBG_warn && current>target)
+ {
+ DBG (DBG_warn,"%s: short slope table, failed to reach %d. target too low ?\n",__FUNCTION__,target);
+ }
+ if(i<3 && DBG_LEVEL >= DBG_warn)
+ {
+ DBG (DBG_warn,"%s: short slope table, failed to reach %d. target too high ?\n",__FUNCTION__,target);
+ }
+
+ /* align on factor */
+ while(i%factor!=0)
+ {
+ slope[i+1]=slope[i];
+ sum+=slope[i];
+ i++;
+ }
+
+ /* ensure minimal slope size */
+ while(i<2*factor)
+ {
+ slope[i+1]=slope[i];
+ sum+=slope[i];
+ i++;
+ }
+
+ /* return used steps and acceleration sum */
+ *steps=i/factor;
+ return sum;
+}
+
+/** @brief returns the lowest possible ydpi for the device
+ * Parses device entry to find lowest motor dpi.
+ * @param dev device description
+ * @return lowest motor resolution
+ */
+int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev)
+{
+ int min=20000;
+ int i=0;
+
+ while(dev->model->ydpi_values[i]!=0)
+ {
+ if(dev->model->ydpi_values[i]<min)
+ {
+ min=dev->model->ydpi_values[i];
+ }
+ i++;
+ }
+ return min;
+}
+
+/** @brief returns the lowest possible dpi for the device
+ * Parses device entry to find lowest motor or sensor dpi.
+ * @param dev device description
+ * @return lowest motor resolution
+ */
+int sanei_genesys_get_lowest_dpi(Genesys_Device *dev)
+{
+ int min=20000;
+ int i=0;
+
+ while(dev->model->ydpi_values[i]!=0)
+ {
+ if(dev->model->ydpi_values[i]<min)
+ {
+ min=dev->model->ydpi_values[i];
+ }
+ i++;
+ }
+ i=0;
+ while(dev->model->xdpi_values[i]!=0)
+ {
+ if(dev->model->xdpi_values[i]<min)
+ {
+ min=dev->model->xdpi_values[i];
+ }
+ i++;
+ }
+ return min;
+}
+
+/** @brief check is a cache entry may be used
+ * Compares current settings with the cache entry and return
+ * SANE_TRUE if they are compatible.
+ */
+SANE_Status
+sanei_genesys_is_compatible_calibration (Genesys_Device * dev,
+ Genesys_Calibration_Cache * cache,
+ int for_overwrite)
+{
+#ifdef HAVE_SYS_TIME_H
+ struct timeval time;
+#endif
+ int compatible = 1, resolution;
+ SANE_Status status;
+
+ DBGSTART;
+
+ if(dev->model->cmd_set->calculate_current_setup==NULL)
+ {
+ DBG (DBG_proc,
+ "sanei_genesys_is_compatible_calibration: no calculate_setup, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ status = dev->model->cmd_set->calculate_current_setup (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "sanei_genesys_is_compatible_calibration: failed to calculate current setup: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ dev->current_setup.scan_method = dev->settings.scan_method;
+
+ DBG (DBG_proc, "sanei_genesys_is_compatible_calibration: checking\n");
+
+ /* a calibration cache is compatible if color mode and x dpi match the user
+ * requested scan. In the case of CIS scanners, dpi isn't a criteria */
+ if (dev->model->is_cis == SANE_FALSE)
+ {
+ resolution = dev->settings.xres;
+ if(resolution>dev->sensor.optical_res)
+ {
+ resolution=dev->sensor.optical_res;
+ }
+ compatible = (resolution == ((int) cache->used_setup.xres));
+ }
+ else
+ {
+ resolution=sanei_genesys_compute_dpihw(dev,dev->settings.xres);
+ compatible = (resolution == ((int) sanei_genesys_compute_dpihw(dev,cache->used_setup.xres)));
+ }
+ if (dev->current_setup.half_ccd != cache->used_setup.half_ccd)
+ {
+ DBG (DBG_io,
+ "sanei_genesys_is_compatible_calibration: half_ccd=%d, used=%d\n",
+ dev->current_setup.half_ccd, cache->used_setup.half_ccd);
+ compatible = 0;
+ }
+ if (dev->current_setup.scan_method != cache->used_setup.scan_method)
+ {
+ DBG (DBG_io,
+ "sanei_genesys_is_compatible_calibration: current method=%d, used=%d\n",
+ dev->current_setup.scan_method, cache->used_setup.scan_method);
+ compatible = 0;
+ }
+ if (!compatible)
+ {
+ DBG (DBG_proc,
+ "sanei_genesys_is_compatible_calibration: completed, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ /* a cache entry expires after 60 minutes for non sheetfed scanners */
+ /* this is not taken into account when overwriting cache entries */
+#ifdef HAVE_SYS_TIME_H
+ if(for_overwrite == SANE_FALSE)
+ {
+ gettimeofday (&time, NULL);
+ if ((time.tv_sec - cache->last_calibration > 60 * 60)
+ && (dev->model->is_sheetfed == SANE_FALSE)
+ && (dev->settings.scan_method == SCAN_METHOD_FLATBED))
+ {
+ DBG (DBG_proc,
+ "sanei_genesys_is_compatible_calibration: expired entry, non compatible cache\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ }
+#endif
+
+ DBGCOMPLETED;
+ return SANE_STATUS_GOOD;
+}
+
+
+/** @brief compute maximum line distance shift
+ * compute maximum line distance shift for the motor and sensor
+ * combination. Line distance shift is the distance between different
+ * color component of CCD sensors. Since these components aren't at
+ * the same physical place, they scan diffrent lines. Software must
+ * take this into account to accurately mix color data.
+ * @param dev device session to compute max_shift for
+ * @param channels number of color channels for the scan
+ * @param yres motor resolution used for the scan
+ * @param flags scan flags
+ * @return 0 or line distance shift
+ */
+int sanei_genesys_compute_max_shift(Genesys_Device *dev,
+ int channels,
+ int yres,
+ int flags)
+{
+ int max_shift;
+
+ max_shift=0;
+ if (channels > 1 && !(flags & SCAN_FLAG_IGNORE_LINE_DISTANCE))
+ {
+ max_shift = dev->ld_shift_r;
+ if (dev->ld_shift_b > max_shift)
+ max_shift = dev->ld_shift_b;
+ if (dev->ld_shift_g > max_shift)
+ max_shift = dev->ld_shift_g;
+ max_shift = (max_shift * yres) / dev->motor.base_ydpi;
+ }
+ return max_shift;
+}
+
+/** @brief build lookup table for digital enhancements
+ * Function to build a lookup table (LUT), often
+ used by scanners to implement brightness/contrast/gamma
+ or by backends to speed binarization/thresholding
+
+ offset and slope inputs are -127 to +127
+
+ slope rotates line around central input/output val,
+ 0 makes horizontal line
+
+ pos zero neg
+ . x . . x
+ . x . . x
+ out . x .xxxxxxxxxxx . x
+ . x . . x
+ ....x....... ............ .......x....
+ in in in
+
+ offset moves line vertically, and clamps to output range
+ 0 keeps the line crossing the center of the table
+
+ high low
+ . xxxxxxxx .
+ . x .
+ out x . x
+ . . x
+ ............ xxxxxxxx....
+ in in
+
+ out_min/max provide bounds on output values,
+ useful when building thresholding lut.
+ 0 and 255 are good defaults otherwise.
+ * @param lut pointer where to store the generated lut
+ * @param in_bits number of bits for in values
+ * @param out_bits number of bits of out values
+ * @param out_min minimal out value
+ * @param out_max maximal out value
+ * @param slope slope of the generated data
+ * @param offset offset of the generated data
+ */
+SANE_Status
+sanei_genesys_load_lut (unsigned char * lut,
+ int in_bits,
+ int out_bits,
+ int out_min,
+ int out_max,
+ int slope,
+ int offset)
+{
+ SANE_Status ret = SANE_STATUS_GOOD;
+ int i, j;
+ double shift, rise;
+ int max_in_val = (1 << in_bits) - 1;
+ int max_out_val = (1 << out_bits) - 1;
+ uint8_t *lut_p8 = lut;
+ uint16_t *lut_p16 = (uint16_t *) lut;
+
+ DBGSTART;
+
+ /* slope is converted to rise per unit run:
+ * first [-127,127] to [-.999,.999]
+ * then to [-PI/4,PI/4] then [0,PI/2]
+ * then take the tangent (T.O.A)
+ * then multiply by the normal linear slope
+ * because the table may not be square, i.e. 1024x256*/
+ rise = tan ((double) slope / 128 * M_PI_4 + M_PI_4) * max_out_val / max_in_val;
+
+ /* line must stay vertically centered, so figure
+ * out vertical offset at central input value */
+ shift = (double) max_out_val / 2 - (rise * max_in_val / 2);
+
+ /* convert the user offset setting to scale of output
+ * first [-127,127] to [-1,1]
+ * then to [-max_out_val/2,max_out_val/2]*/
+ shift += (double) offset / 127 * max_out_val / 2;
+
+ for (i = 0; i <= max_in_val; i++)
+ {
+ j = rise * i + shift;
+
+ /* cap data to required range */
+ if (j < out_min)
+ {
+ j = out_min;
+ }
+ else if (j > out_max)
+ {
+ j = out_max;
+ }
+
+ /* copy result according to bit depth */
+ if (out_bits <= 8)
+ {
+ *lut_p8 = j;
+ lut_p8++;
+ }
+ else
+ {
+ *lut_p16 = j;
+ lut_p16++;
+ }
+ }
+
+ DBGCOMPLETED;
+ return ret;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */