summaryrefslogtreecommitdiff
path: root/backend/p5_device.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/p5_device.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/p5_device.c')
-rw-r--r--backend/p5_device.c1611
1 files changed, 1611 insertions, 0 deletions
diff --git a/backend/p5_device.c b/backend/p5_device.c
new file mode 100644
index 0000000..c065ca2
--- /dev/null
+++ b/backend/p5_device.c
@@ -0,0 +1,1611 @@
+/**
+ * Description of the Primax PagePartner model
+ */
+static P5_Model pagepartner_model = {
+ "Primax PagePartner",
+ "Primax",
+ "PagePartner",
+ SANE_I18N ("sheetfed scanner"),
+
+ {300, 200, 150, 100, 0},
+ /* 500 seems also possible */
+ {600, 400, 300, 200, 150, 100, 0},
+
+ 300,
+ 600,
+ 100,
+ 100,
+ 16,
+
+ SANE_FIX (0.0),
+ SANE_FIX (0.0),
+ SANE_FIX (215.9),
+ SANE_FIX (300.0),
+};
+
+#ifdef HAVE_LINUX_PPDEV_H
+static char *
+addr_name (uint16_t addr)
+{
+ switch (addr)
+ {
+ case DATA:
+ return "DATA";
+ break;
+ case STATUS:
+ return "STATUS";
+ break;
+ case CONTROL:
+ return "CONTROL";
+ break;
+ case EPPADR:
+ return "EPPADR";
+ break;
+ case EPPDATA:
+ return "EPPDATA";
+ break;
+ default:
+ return "*ERROR*";
+ }
+}
+#endif
+
+/** @brief low level hardware access functions
+ * @{
+ */
+
+static uint8_t
+inb (int fd, uint16_t addr)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ uint8_t val = 0xff;
+ int rc, mode = 0xff;
+
+ switch (addr)
+ {
+ case DATA:
+ rc = ioctl (fd, PPRDATA, &val);
+ break;
+ case STATUS:
+ rc = ioctl (fd, PPRSTATUS, &val);
+ break;
+ case CONTROL:
+ rc = ioctl (fd, PPRCONTROL, &val);
+ break;
+ case EPPDATA:
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+#endif
+ rc = read (fd, &val, 1);
+ break;
+ default:
+ DBG (DBG_error, "inb(%s) escaped ppdev\n", addr_name (addr));
+ return 0xFF;
+ }
+ if (rc < 0)
+ {
+ DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
+ }
+ return val;
+#else
+ if(fd && addr)
+ return 0;
+ return 0;
+#endif
+}
+
+static void
+outb (int fd, uint16_t addr, uint8_t value)
+{
+#ifdef HAVE_LINUX_PPDEV_H
+ int rc = 0, mode = 0xff;
+
+ switch (addr)
+ {
+ case DATA:
+ rc = ioctl (fd, PPWDATA, &value);
+ break;
+ case CONTROL:
+ mode = value & 0x20;
+ rc = ioctl (fd, PPDATADIR, &mode);
+ if (!rc)
+ {
+ value = value & 0xDF;
+ rc = ioctl (fd, PPWCONTROL, &value);
+ }
+ break;
+ case EPPDATA:
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &value, 1);
+ break;
+ case EPPADR:
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &value, 1);
+ break;
+ default:
+ DBG (DBG_error, "outb(%s,0x%02x) escaped ppdev\n", addr_name (addr),
+ value);
+ break;
+ }
+ if (rc < 0)
+ {
+ DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
+ }
+#else
+ if(fd && addr && value)
+ return;
+#endif /* HAVE_LINUX_PPDEV_H */
+}
+
+static void
+write_reg (int fd, uint8_t index, uint8_t value)
+{
+ uint8_t idx;
+
+ /* both nibbles hold the same value */
+ idx = index & 0x0F;
+ DBG (DBG_io2, "write_reg(REG%X,0x%x)\n", idx, value);
+ idx = idx << 4 | idx;
+ outb (fd, EPPADR, idx);
+ outb (fd, EPPDATA, value);
+}
+
+static uint8_t
+read_reg (int fd, uint8_t index)
+{
+ uint8_t idx;
+
+ /* both nibbles hold the same value */
+ idx = index & 0x0F;
+ idx = idx << 4 | idx;
+ outb (fd, EPPADR, idx);
+ return inb (fd, EPPDATA);
+}
+
+#ifdef HAVE_LINUX_PPDEV_H
+static int
+read_data (int fd, uint8_t * data, int length)
+{
+ int mode, rc, nb;
+ unsigned char bval;
+
+ bval = REG8;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &bval, 1);
+
+ mode = 1; /* data_reverse */
+ rc = ioctl (fd, PPDATADIR, &mode);
+#ifdef PPSETFLAGS
+ mode = PP_FASTREAD;
+ rc = ioctl (fd, PPSETFLAGS, &mode);
+#endif
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ nb = 0;
+ while (nb < length)
+ {
+ rc = read (fd, data + nb, length - nb);
+ if (rc < 0)
+ {
+ DBG (DBG_error, "memtest: error reading data back!\n");
+ return 0;
+ }
+ else
+ {
+ nb += rc;
+ }
+ }
+
+ return 1;
+}
+
+static void
+index_write_data (int fd, uint8_t index, uint8_t * data, int length)
+{
+ int mode, rc;
+ unsigned char bval;
+
+ bval = index;
+ mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ rc = write (fd, &bval, 1);
+
+ mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
+ rc = ioctl (fd, PPSETMODE, &mode);
+ mode = 0; /* data forward */
+ rc = ioctl (fd, PPDATADIR, &mode);
+ rc = write (fd, data, length);
+ return;
+}
+
+static void
+write_data (int fd, uint8_t * data, int length)
+{
+ index_write_data (fd, REG8, data, length);
+}
+
+static void
+write_reg2 (int fd, uint8_t index, uint16_t value)
+{
+ uint8_t data2[2];
+
+ data2[0] = value & 0xff;
+ data2[1] = value >> 8;
+ index_write_data (fd, index, data2, 2);
+}
+#else
+
+static int
+read_data (int fd, uint8_t * data, int length)
+{
+ if(fd && data && length)
+ return -1;
+ return -1;
+}
+
+static void
+write_data (int fd, uint8_t * data, int length)
+{
+ if(fd && data && length)
+ return;
+}
+
+static void
+write_reg2 (int fd, uint8_t index, uint16_t value)
+{
+ if(fd && index && value)
+ return;
+}
+#endif
+
+/**
+ * @}
+ */
+
+
+/** @brief This function checks a memory buffer.
+ * This function writes at the given memory address then read it back
+ * to check the scanner is correctly working.
+ * @param fd file descriptor used to access hardware
+ * @param addr address where to write and read
+ * @return SANE_TRUE on succes, SANE_FALSE otherwise
+ */
+static int
+memtest (int fd, uint16_t addr)
+{
+ uint8_t sent[256];
+ uint8_t back[256];
+ int i;
+
+ write_reg2 (fd, REG1, addr);
+ for (i = 0; i < 256; i++)
+ {
+ sent[i] = (uint8_t) i;
+ back[i] = 0;
+ }
+ write_data (fd, sent, 256);
+ read_data (fd, back, 256);
+
+ /* check if data read back is the same that the one sent */
+ for (i = 0; i < 256; i++)
+ {
+ if (back[i] != sent[i])
+ {
+ return SANE_FALSE;
+ }
+ }
+
+ return SANE_TRUE;
+}
+
+
+#define INB(k,y,z) val=inb(k,y); if(val!=z) { DBG(DBG_error,"expected 0x%02x, got 0x%02x\n",z, val); return SANE_FALSE; }
+
+/** @brief connect to scanner
+ * This function sends the connect sequence for the scanner.
+ * @param fd filedescriptor of the parallel port communication channel
+ * @return SANE_TRUE in case of success, SANE_FALSE otherwise
+ */
+static int
+connect (int fd)
+{
+ uint8_t val;
+
+ inb (fd, CONTROL);
+ outb (fd, CONTROL, 0x04);
+ outb (fd, DATA, 0x02);
+ INB (fd, DATA, 0x02);
+ outb (fd, DATA, 0x03);
+ INB (fd, DATA, 0x03);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ outb (fd, DATA, 0x03);
+ outb (fd, DATA, 0x83);
+ INB (fd, DATA, 0x83);
+ outb (fd, DATA, 0x82);
+ INB (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0x02);
+ outb (fd, DATA, 0x82);
+ outb (fd, DATA, 0xFF);
+ DBG (DBG_info, "connect() OK...\n");
+ return SANE_TRUE;
+}
+
+static int
+disconnect (int fd)
+{
+ uint8_t val;
+
+ outb (fd, CONTROL, 0x04);
+ outb (fd, DATA, 0x00);
+ INB (fd, DATA, 0x00);
+ outb (fd, DATA, 0x01);
+ INB (fd, DATA, 0x01);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ INB (fd, DATA, 0x81);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x81);
+ INB (fd, DATA, 0x81);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ outb (fd, DATA, 0x01);
+ outb (fd, DATA, 0x81);
+ INB (fd, DATA, 0x81);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ INB (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ outb (fd, DATA, 0x00);
+ outb (fd, DATA, 0x80);
+ inb (fd, CONTROL);
+ outb (fd, CONTROL, 0x0C);
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+setadresses (int fd, uint16_t start, uint16_t end)
+{
+ write_reg (fd, REG3, start & 0xff);
+ write_reg (fd, REG4, start >> 8);
+ write_reg (fd, REG5, end & 0xff);
+ write_reg (fd, REG6, end >> 8);
+ DBG (DBG_io, "setadresses(0x%x,0x%x); OK...\n", start, end);
+}
+
+#ifdef HAVE_LINUX_PPDEV_H
+/** @brief open parallel port device
+ * opens parallel port's low level device in EPP mode
+ * @param devicename nam of the real device or the special value 'auto'
+ * @return file descriptor in cas of successn -1 otherwise
+ */
+static int
+open_pp (const char *devicename)
+{
+ int fd, rc, mode = 0;
+ char *name;
+
+ DBG (DBG_proc, "open_pp: start, devicename=%s\n", devicename);
+ /* TODO improve auto device finding */
+ if (strncmp (devicename, "auto", 4) == 0)
+ {
+ name = strdup("/dev/parport0");
+ }
+ else
+ {
+ name = strdup(devicename);
+ }
+
+ /* open device */
+ fd = open (name, O_RDWR);
+ if (fd < 0)
+ {
+ switch (errno)
+ {
+ case ENOENT:
+#ifdef ENIO
+ case ENXIO:
+#endif
+#ifdef ENODEV
+ case ENODEV:
+#endif
+ DBG (DBG_error, "open_pp: no %s device ...\n", name);
+ break;
+ case EACCES:
+ DBG (DBG_error,
+ "open_pp: current user cannot use existing %s device ...\n",
+ name);
+ break;
+ default:
+ DBG (DBG_error, "open_pp: %s while opening %s\n", strerror (errno),
+ name);
+ }
+ return -1;
+ }
+ free(name);
+
+ /* claim device and set it to EPP */
+ rc = ioctl (fd, PPCLAIM);
+ rc = ioctl (fd, PPGETMODES, &mode);
+ if (mode & PARPORT_MODE_PCSPP)
+ DBG (DBG_io, "PARPORT_MODE_PCSPP\n");
+ if (mode & PARPORT_MODE_TRISTATE)
+ DBG (DBG_io, "PARPORT_MODE_TRISTATE\n");
+ if (mode & PARPORT_MODE_EPP)
+ DBG (DBG_io, "PARPORT_MODE_EPP\n");
+ if (mode & PARPORT_MODE_ECP)
+ DBG (DBG_io, "PARPORT_MODE_ECP\n");
+ if (mode & PARPORT_MODE_COMPAT)
+ DBG (DBG_io, "PARPORT_MODE_COMPAT\n");
+ if (mode & PARPORT_MODE_DMA)
+ DBG (DBG_io, "PARPORT_MODE_DMA\n");
+ if (mode & PARPORT_MODE_EPP)
+ {
+ mode = IEEE1284_MODE_EPP;
+ }
+ else
+ {
+ /*
+ if (mode & PARPORT_MODE_ECP)
+ {
+ mode = IEEE1284_MODE_ECP;
+ }
+ else
+ */
+ {
+ mode = -1;
+ }
+ }
+ if (mode == -1)
+ {
+ DBG (DBG_error, "open_pp: no EPP mode, giving up ...\n");
+ rc = ioctl (fd, PPRELEASE);
+ close (fd);
+ return -1;
+ }
+ rc = ioctl (fd, PPNEGOT, &mode);
+ rc = ioctl (fd, PPSETMODE, &mode);
+ DBG (DBG_proc, "open_pp: exit\n");
+ return fd;
+}
+
+/** close low level device
+ * release and close low level hardware device
+ */
+static void
+close_pp (int fd)
+{
+ int mode = IEEE1284_MODE_COMPAT;
+
+ if (fd > 2)
+ {
+ ioctl (fd, PPNEGOT, &mode);
+ ioctl (fd, PPRELEASE);
+ close (fd);
+ }
+}
+
+#else /* HAVE_LINUX_PPDEV_H */
+
+static int
+open_pp (const char *devicename)
+{
+ if(devicename)
+ return -1;
+ return -1;
+}
+
+static void
+close_pp (int fd)
+{
+ if(fd)
+ return;
+}
+#endif /* HAVE_LINUX_PPDEV_H */
+
+/** @brief test if a document is inserted
+ * Test if a document is inserted by reading register E
+ * @param fd file descriptor to access scanner
+ * @return SANE_STATUS_NO_DOCS if no document or SANE_STATUS_GOOD
+ * if something is present.
+ */
+static SANE_Status
+test_document (int fd)
+{
+ int detector;
+
+ /* check for document presence 0xC6: present, 0xC3 no document */
+ detector = read_reg (fd, REGE);
+ DBG (DBG_io, "test_document: detector=0x%02X\n", detector);
+
+ /* document inserted */
+ if (detector & 0x04)
+ return SANE_STATUS_GOOD;
+
+ return SANE_STATUS_NO_DOCS;
+}
+
+/**
+ * return the amount of scanned data available
+ * @param fd file descriptor to access scanner
+ * @return avaible byte number
+ */
+static int
+available_bytes (int fd)
+{
+ int counter;
+
+ /* read the number of 256 bytes block of scanned data */
+ counter = read_reg (fd, REG9);
+ DBG (DBG_io, "available_bytes: available_bytes=0x%02X\n", counter);
+ return 256 * counter;
+}
+
+static SANE_Status
+build_correction (P5_Device * dev, unsigned int dpi, unsigned int mode,
+ unsigned int start, unsigned int width)
+{
+ unsigned int i, j, shift, step;
+
+ DBG (DBG_proc, "build_correction: start=%d, width=%d\n", start, width);
+ DBG (DBG_trace, "build_correction: dpi=%d, mode=%d\n", dpi, mode);
+
+ /* loop on calibration data to find the matching one */
+ j = 0;
+ while (dev->calibration_data[j]->dpi != dpi)
+ {
+ j++;
+ if (j > MAX_RESOLUTIONS)
+ {
+ DBG (DBG_error, "build_correction: couldn't find calibration!\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ if (dev->gain != NULL)
+ {
+ free (dev->gain);
+ dev->gain = NULL;
+ }
+ if (dev->offset != NULL)
+ {
+ free (dev->offset);
+ dev->offset = NULL;
+ }
+ dev->gain = (float *) malloc (width * sizeof (float));
+ if (dev->gain == NULL)
+ {
+ DBG (DBG_error,
+ "build_correction: failed to allocate memory for gain!\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->offset = (uint8_t *) malloc (width);
+ if (dev->offset == NULL)
+ {
+ DBG (DBG_error,
+ "build_correction: failed to allocate memory for offset!\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* compute starting point of calibration data to use */
+ shift = start;
+ step = 1;
+ if (mode == MODE_GRAY)
+ {
+ /* we use green data */
+ shift += 1;
+ step = 3;
+ }
+ for (i = 0; i < width; i += step)
+ {
+ if (dev->calibration_data[j]->white_data[shift + i] -
+ dev->calibration_data[0]->black_data[shift + i] > BLACK_LEVEL)
+ {
+ dev->gain[i] =
+ WHITE_TARGET /
+ ((float)
+ (dev->calibration_data[j]->white_data[shift + i] -
+ dev->calibration_data[j]->black_data[shift + i]));
+ dev->offset[i] = dev->calibration_data[j]->black_data[shift + i];
+ }
+ else
+ {
+ dev->gain[i] = 1.0;
+ dev->offset[i] = 0;
+ }
+ }
+ return SANE_STATUS_GOOD;
+ DBG (DBG_proc, "build_correction: end\n");
+}
+
+/** @brief start up a real scan
+ * This function starts the scan with the given parameters.
+ * @param dev device describing hardware
+ * @param mode color, gray level or lineart.
+ * @param dpi desired scan resolution.
+ * @param startx coordinate of the first pixel to scan in
+ * scan's resolution coordinate
+ * @param width width of the scanned area
+ * scanner's physical scan aread.
+ * @return SANE_STATUS_GOOD if scan is successfully started
+ */
+static SANE_Status
+start_scan (P5_Device * dev, int mode, unsigned int dpi, unsigned int startx,
+ unsigned int width)
+{
+ uint8_t reg0=0;
+ uint8_t reg2=0;
+ uint8_t regF=0;
+ uint16_t addr=0;
+ uint16_t start, end;
+ unsigned int xdpi;
+
+ DBG (DBG_proc, "start_scan: start \n");
+ DBG (DBG_io, "start_scan: startx=%d, width=%d, dpi=%d\n", startx, width,
+ dpi);
+
+ /** @brief register values
+ * - reg2 : reg2 seems related to x dpi and provides only 100,
+ * 150, 200 and 300 resolutions.
+ * - regF : lower nibble gives y dpi resolution ranging from 150
+ * to 1200 dpi.
+ */
+ xdpi = dpi;
+ switch (dpi)
+ {
+ case 100:
+ reg2 = 0x90;
+ regF = 0xA2;
+ break;
+ case 150:
+ reg2 = 0x10;
+ regF = 0xA4;
+ break;
+ case 200:
+ reg2 = 0x80;
+ regF = 0xA6;
+ break;
+ case 300:
+ reg2 = 0x00;
+ regF = 0xA8;
+ break;
+ case 400:
+ reg2 = 0x80; /* xdpi=200 */
+ regF = 0xAA;
+ xdpi = 200;
+ break;
+ case 500:
+ reg2 = 0x00;
+ regF = 0xAC;
+ xdpi = 300;
+ break;
+ case 600:
+ reg2 = 0x00;
+ regF = 0xAE;
+ xdpi = 300;
+ break;
+ }
+
+ switch (mode)
+ {
+ case MODE_COLOR:
+ reg0 = 0x00;
+ addr = 0x0100;
+ break;
+ case MODE_GRAY:
+ /* green channel only */
+ reg0 = 0x20;
+ addr = 0x0100;
+ break;
+ case MODE_LINEART:
+ reg0 = 0x40;
+ addr = 0x0908;
+ break;
+ }
+
+ write_reg (dev->fd, REG1, 0x01);
+ write_reg (dev->fd, REG7, 0x00);
+ write_reg (dev->fd, REG0, reg0);
+ write_reg (dev->fd, REG1, 0x00);
+ write_reg (dev->fd, REGF, regF);
+ /* the memory addr used to test need not to be related
+ * to resolution, 0x0100 could be always used */
+ /* TODO get rid of it */
+ memtest (dev->fd, addr);
+
+ /* handle case where dpi>xdpi */
+ start = startx;
+ if (dpi > xdpi)
+ {
+ width = (width * xdpi) / dpi;
+ start = (startx * xdpi) / dpi;
+ }
+
+ /* compute and set start addr */
+ if (mode == MODE_COLOR)
+ {
+ start = start * 3;
+ width = width * 3;
+ }
+ end = start + width + 1;
+
+ /* build calibration data for the scan */
+ if (dev->calibrated)
+ {
+ build_correction (dev, xdpi, mode, start, width);
+ }
+
+ setadresses (dev->fd, start, end);
+
+ write_reg (dev->fd, REG1, addr >> 8);
+ write_reg (dev->fd, REG2, reg2);
+ regF = (regF & 0x0F) | 0x80;
+ write_reg (dev->fd, REGF, regF);
+ write_reg (dev->fd, REG0, reg0);
+ if (mode == MODE_LINEART)
+ {
+ write_reg (dev->fd, 0x07, 0x04);
+ }
+ else
+ {
+ write_reg (dev->fd, 0x07, 0x00);
+ }
+ write_reg (dev->fd, REG1, addr >> 8);
+ write_reg2 (dev->fd, REG1, addr);
+ write_reg (dev->fd, REGF, regF | 0x01);
+ write_reg (dev->fd, REG0, reg0 | 0x0C);
+ if (mode == MODE_LINEART)
+ {
+ write_reg (dev->fd, REG1, 0x19);
+ }
+ else
+ {
+ write_reg (dev->fd, REG1, 0x11);
+ }
+ DBG (DBG_proc, "start_scan: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/** read a line of scan data
+ * @param dev device to read
+ * @param data pointer where to store data
+ * @param length total bytes to read on one line
+ * @param ltr total number of lines to read
+ * @param retry signals that the function must read as much lines it can
+ * @param x2 tells that lines must be enlarged by a 2 factor
+ * @param mode COLOR_MODE if color mode
+ * @returns number of data lines read, -1 in case of error
+ */
+static int
+read_line (P5_Device * dev, uint8_t * data, size_t length, int ltr,
+ SANE_Bool retry, SANE_Bool x2, int mode, SANE_Bool correction)
+{
+ uint8_t counter, read, cnt;
+ uint8_t inbuffer[MAX_SENSOR_PIXELS * 2 * 3 + 2];
+ unsigned int i, factor;
+ float val;
+
+ DBG (DBG_proc, "read_line: trying to read %d lines of %lu bytes\n", ltr,
+ (unsigned long)length);
+
+ counter = read_reg (dev->fd, REG9);
+ DBG (DBG_io, "read_line: %d bytes available\n", counter * 256);
+ read = 0;
+ if (x2 == SANE_FALSE)
+ {
+ factor = 1;
+ }
+ else
+ {
+ factor = 2;
+ }
+
+ /* in retry mode we read until not enough data, but in no retry
+ * read only one line , counter give us 256 bytes block available
+ * and we want an number multiple of color channels */
+ cnt = (255 + length / factor) / 256;
+ while ((counter > cnt && retry == 1) || (counter > cnt && read == 0))
+ {
+ /* read data from scanner, first and last byte aren't picture data */
+ read_data (dev->fd, inbuffer, length / factor + 2);
+
+ /* image correction */
+ if (correction == SANE_TRUE)
+ {
+ for (i = 0; i < length / factor; i++)
+ {
+ val = inbuffer[i + 1] - dev->offset[i];
+ if (val > 0)
+ {
+ val = val * dev->gain[i];
+ if (val < 255)
+ inbuffer[i + 1] = val;
+ else
+ inbuffer[i + 1] = 255;
+ }
+ else
+ {
+ inbuffer[i + 1] = 0;
+ }
+ }
+ }
+
+ /* handle horizontal data doubling */
+ if (x2 == SANE_FALSE)
+ {
+ memcpy (data + read * length, inbuffer + 1, length);
+ }
+ else
+ {
+ if (mode == MODE_COLOR)
+ {
+ for (i = 0; i < length / factor; i += 3)
+ {
+ data[read * length + i * factor] = inbuffer[i + 1];
+ data[read * length + i * factor + 1] = inbuffer[i + 2];
+ data[read * length + i * factor + 2] = inbuffer[i + 3];
+ data[read * length + i * factor + 3] = inbuffer[i + 1];
+ data[read * length + i * factor + 4] = inbuffer[i + 2];
+ data[read * length + i * factor + 5] = inbuffer[i + 3];
+ }
+ }
+ else
+ {
+ for (i = 0; i < length / factor; i++)
+ {
+ data[read * length + i * factor] = inbuffer[i + 1];
+ data[read * length + i * factor + 1] = inbuffer[i + 1];
+ }
+ }
+ }
+ read++;
+ if (retry == SANE_TRUE)
+ {
+ read_reg (dev->fd, REGF);
+ read_reg (dev->fd, REGA);
+ read_reg (dev->fd, REG9);
+ counter = read_reg (dev->fd, REG9);
+ read_reg (dev->fd, REGA);
+ if (read >= ltr)
+ {
+ DBG (DBG_io, "read_line returning %d lines\n", read);
+ return read;
+ }
+ counter = read_reg (dev->fd, REG9);
+ }
+ }
+ read_reg (dev->fd, REGF);
+ read_reg (dev->fd, REGA);
+ read_reg (dev->fd, REG9);
+ counter = read_reg (dev->fd, REG9);
+ read_reg (dev->fd, REGA);
+ DBG (DBG_io, "read_line returning %d lines\n", read);
+ return read;
+}
+
+
+static SANE_Status
+eject (int fd)
+{
+ int detector;
+
+ DBG (DBG_proc, "eject: start ...\n");
+
+ do
+ {
+ write_reg2 (fd, REG1, 0x1110);
+ detector = read_reg (fd, REGE);
+ detector = read_reg (fd, REGE);
+ }
+ while ((detector & 0x04) != 0);
+ write_reg (fd, REG0, 0x00);
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REGF, 0x82);
+ write_reg (fd, REG7, 0x00);
+
+ DBG (DBG_proc, "eject: end.\n");
+ return SANE_STATUS_GOOD;
+}
+
+/** @brief wait for document to be present in feeder
+ * Polls document sensor until something is present. Give up after 20 seconds
+ * @param fd file descriptor of the physical device
+ */
+/* static int
+wait_document (int fd, uint8_t detector)
+{
+ int count = 0;
+ uint8_t val;
+
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REG7, 0x00);
+ detector = read_reg (fd, REGE);
+ while (detector == 0xc3 && count < 20)
+ {
+ sleep (1);
+ count++;
+ detector = read_reg (fd, REGE);
+ }
+ setadresses (fd, 0x002d, 0x09c7);
+ write_reg (fd, REG1, 0x00);
+ write_reg (fd, REG2, 0x90);
+ write_reg (fd, REGF, 0x82);
+ write_reg (fd, REG0, 0x00);
+ val = inb (fd, STATUS) & 0xf8;
+ if (val != 0xf8)
+ {
+ DBG (DBG_error, "wait_document: unexpected STATUS value 0x%02x instead of 0xf8", val);
+ }
+ if (count >= 20)
+ {
+ DBG (DBG_error, "wait_document: failed to detect document!\n");
+ return 0;
+ }
+ return 1;
+} */
+
+/** @brief move at 150 dpi
+ * move the paper at 150 dpi motor speed by the amount specified
+ * @params dev pointer to the device structure
+ */
+static SANE_Status
+move (P5_Device * dev)
+{
+ int skip, done, read, count;
+ SANE_Status status = SANE_STATUS_GOOD;
+ unsigned char buffer[256];
+
+ DBG (DBG_proc, "move: start\n");
+
+ /* compute number of lines to skip */
+ skip = dev->ystart;
+
+ /* works, but remains to be explained ... */
+ if (dev->ydpi > 300)
+ skip = skip / 2;
+
+ DBG (DBG_io, "move: skipping %d lines at %d dpi\n", skip, dev->ydpi);
+
+ /* we do a real scan of small width, discarding data */
+ done = 0;
+ status = start_scan (dev, MODE_GRAY, dev->ydpi, 0, 256);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "move: failed to start scan\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ do
+ {
+ /* test if document left the feeder */
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS)
+ {
+ DBG (DBG_info,
+ "move: document was shorter than the required move\n");
+ return SANE_STATUS_INVAL;
+ }
+ /* test if data is available */
+ count = available_bytes (dev->fd);
+ if (count)
+ {
+ read =
+ read_line (dev, buffer, 256, 1, SANE_FALSE, SANE_FALSE,
+ MODE_GRAY, SANE_FALSE);
+ if (read == -1)
+ {
+ DBG (DBG_error, "move: failed to read data\n");
+ return SANE_STATUS_INVAL;
+ }
+ done += read;
+ }
+ }
+ while (done < skip);
+
+ /* reset scanner */
+ write_reg2 (dev->fd, REG1, 0x1110);
+ count = read_reg (dev->fd, REGE);
+ count = read_reg (dev->fd, REGE);
+ write_reg (dev->fd, REG0, 0x00);
+ write_reg (dev->fd, REG1, 0x00);
+ write_reg (dev->fd, REGF, 0x82);
+ write_reg (dev->fd, REG7, 0x00);
+
+ DBG (DBG_proc, "move: exit\n");
+ return status;
+}
+
+/** clean up calibration data
+ * @param dev device to clean up
+ */
+static void
+cleanup_calibration (P5_Device * dev)
+{
+ int i;
+
+ for (i = 0; i < MAX_RESOLUTIONS * 2; i++)
+ {
+ if (dev->calibration_data[i] != NULL)
+ {
+ free (dev->calibration_data[i]);
+ dev->calibration_data[i] = NULL;
+ }
+ }
+ dev->calibrated = SANE_FALSE;
+}
+
+/** detect a black scan line
+ * parses the given buffer and retrun SANE_TRUE if the line is an
+ * acceptable black line for calibration
+ * @param buffer data line to parse
+ * @param pixels number of pixels
+ * @param mode MODE_COLOR or MODE_GRAY
+ * @returns SANE_TRUE if it is considered as a white line
+ */
+static SANE_Bool
+is_black_line (uint8_t * buffer, unsigned int pixels, int mode)
+{
+ unsigned int i, start, end, count, width;
+
+ /* compute width in bytes */
+ if (mode == MODE_COLOR)
+ {
+ width = pixels * 3;
+ }
+ else
+ {
+ width = pixels;
+ }
+
+ /* we allow the calibration target to be narrower than full width, ie
+ * black margin at both ends of the line */
+ start = (5 * width) / 100;
+ end = (95 * width) / 100;
+ count = 0;
+
+ /* count number of black bytes */
+ for (i = start; i < end; i++)
+ {
+ if (buffer[i] > BLACK_LEVEL)
+ {
+ count++;
+ }
+ }
+
+ /* we allow 3% black pixels maximum */
+ if (count > (3 * width) / 100)
+ {
+ DBG (DBG_io, "is_black_line=SANE_FALSE\n");
+ return SANE_FALSE;
+ }
+
+ DBG (DBG_io, "is_black_line=SANE_TRUE\n");
+ return SANE_TRUE;
+}
+
+/** detect a white scan line
+ * parses the given buffer and retrun SANE_TRUE if the line is an
+ * acceptable white line for calibration
+ * @param buffer data line to parse
+ * @param pixels number of pixels
+ * @param mode MODE_COLOR or MODE_GRAY
+ * @returns SANE_TRUE if it is considered as a white line
+ */
+static SANE_Bool
+is_white_line (uint8_t * buffer, unsigned int pixels, int mode)
+{
+ unsigned int i, start, end, count, width;
+
+ /* compute width in bytes */
+ if (mode == MODE_COLOR)
+ {
+ width = pixels * 3;
+ }
+ else
+ {
+ width = pixels;
+ }
+
+ /* we allow the calibration target to be narrower than full width, ie
+ * black margin at both ends of the line */
+ start = (5 * width) / 100;
+ end = (95 * width) / 100;
+ count = 0;
+
+ /* count number of black bytes */
+ for (i = start; i < end; i++)
+ {
+ if (buffer[i] < BLACK_LEVEL)
+ {
+ count++;
+ }
+ }
+
+ /* we allow 3% black pixels maximum */
+ if (count > (3 * width) / 100)
+ {
+ DBG (DBG_io, "is_white_line=SANE_FALSE\n");
+ return SANE_FALSE;
+ }
+
+ DBG (DBG_io, "is_white_line=SANE_TRUE\n");
+ return SANE_TRUE;
+}
+
+/* ------------------------------------------------------------------------- */
+/* writes gray data to a pnm file */
+/*
+static void
+write_gray_data (unsigned char *image, char *name, SANE_Int width,
+ SANE_Int height)
+{
+ FILE *fdbg = NULL;
+
+ fdbg = fopen (name, "wb");
+ if (fdbg == NULL)
+ return;
+ fprintf (fdbg, "P5\n%d %d\n255\n", width, height);
+ fwrite (image, width, height, fdbg);
+ fclose (fdbg);
+}
+*/
+
+/* ------------------------------------------------------------------------- */
+/* writes rgb data to a pnm file */
+static void
+write_rgb_data (char *name, unsigned char *image, SANE_Int width,
+ SANE_Int height)
+{
+ FILE *fdbg = NULL;
+
+ fdbg = fopen (name, "wb");
+ if (fdbg == NULL)
+ return;
+ fprintf (fdbg, "P6\n%d %d\n255\n", width, height);
+ fwrite (image, width * 3, height, fdbg);
+ fclose (fdbg);
+}
+
+/** give calibration file name
+ * computes the calibration file name to use based on the
+ * backend name and device
+ */
+static char *
+calibration_file (const char *devicename)
+{
+ char *ptr = NULL;
+ char tmp_str[PATH_MAX];
+
+ ptr = getenv ("HOME");
+ if (ptr != NULL)
+ {
+ sprintf (tmp_str, "%s/.sane/p5-%s.cal", ptr, devicename);
+ }
+ else
+ {
+ ptr = getenv ("TMPDIR");
+ if (ptr != NULL)
+ {
+ sprintf (tmp_str, "%s/p5-%s.cal", ptr, devicename);
+ }
+ else
+ {
+ sprintf (tmp_str, "/tmp/p5-%s.cal", devicename);
+ }
+ }
+ DBG (DBG_trace, "calibration_file: using >%s< for calibration file name\n",
+ tmp_str);
+ return strdup (tmp_str);
+}
+
+/** restore calibration data
+ * restore calibration data by loading previously saved calibration data
+ * @param dev device to restore
+ * @return SANE_STATUS_GOOD on success, otherwise error code
+ */
+static SANE_Status
+restore_calibration (P5_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ char *fname = NULL;
+ FILE *fcalib = NULL;
+ size_t size;
+ int i;
+
+ DBG (DBG_proc, "restore_calibration: start\n");
+ cleanup_calibration (dev);
+ fname = calibration_file (dev->model->name);
+ fcalib = fopen (fname, "rb");
+ if (fcalib == NULL)
+ {
+ DBG (DBG_error, "restore_calibration: failed to open %s!\n", fname);
+ free (fname);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* loop filling calibration data until EOF reached */
+ i = 0;
+ while (!feof (fcalib) && (i < 2 * MAX_RESOLUTIONS))
+ {
+ dev->calibration_data[i] = malloc (sizeof (P5_Calibration_Data));
+ if (dev->calibration_data[i] == NULL)
+ {
+ cleanup_calibration (dev);
+ free (fname);
+ fclose (fcalib);
+ DBG (DBG_error,
+ "restore_calibration: failed to allocate memory for calibration\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ size =
+ fread (dev->calibration_data[i], 1, sizeof (P5_Calibration_Data),
+ fcalib);
+ if (feof (fcalib))
+ {
+ free (dev->calibration_data[i]);
+ dev->calibration_data[i] = NULL;
+ }
+ else if (size != sizeof (P5_Calibration_Data))
+ {
+ cleanup_calibration (dev);
+ free (fname);
+ fclose (fcalib);
+ DBG (DBG_error, "restore_calibration: failed to read from file\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_trace,
+ "restore_calibration: read 1 calibration structure from file\n");
+ i++;
+ }
+
+ dev->calibrated = SANE_TRUE;
+ fclose (fcalib);
+ free (fname);
+
+ DBG (DBG_proc, "restore_calibration: end\n");
+ return status;
+}
+
+/** save calibration data
+ * save calibration data from memory to file
+ * @param dev device calibration to save
+ * @return SANE_STATUS_GOOD on success, otherwise error code
+ */
+static SANE_Status
+save_calibration (P5_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ char *fname = NULL;
+ FILE *fcalib = NULL;
+ int i;
+ size_t size;
+
+ DBG (DBG_proc, "save_calibration: start\n");
+ fname = calibration_file (dev->model->name);
+ fcalib = fopen (fname, "wb");
+ if (fcalib == NULL)
+ {
+ DBG (DBG_error, "save_calibration: failed to open %s!\n", fname);
+ free (fname);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* loop filling calibration data until EOF reached */
+ i = 0;
+ while (dev->calibration_data[i] != NULL && (i < 2 * MAX_RESOLUTIONS))
+ {
+ size =
+ fwrite (dev->calibration_data[i], sizeof (P5_Calibration_Data), 1,
+ fcalib);
+ if (size != sizeof (P5_Calibration_Data))
+ {
+ free (fname);
+ fclose (fcalib);
+ DBG (DBG_error, "save_calibration: failed to write to file\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ DBG (DBG_trace,
+ "save_calibration: wrote 1 calibration structure to file\n");
+ i++;
+ }
+
+ fclose (fcalib);
+ free (fname);
+
+ DBG (DBG_proc, "save_calibration: end\n");
+ return status;
+}
+
+/** calibrate scanner
+ * calibrates scanner by scanning a white sheet to get
+ * reference data. The black reference data is extracted from the lines
+ * that precede the physical document.
+ * Calibration is done at 300 color, then data is built for other modes
+ * and resolutions.
+ * @param dev device to calibrate
+ */
+static SANE_Status
+sheetfed_calibration (P5_Device * dev)
+{
+ uint8_t buffer[MAX_SENSOR_PIXELS * 3];
+ uint16_t white_data[MAX_SENSOR_PIXELS * 3];
+ uint16_t black_data[MAX_SENSOR_PIXELS * 3];
+ unsigned int i, j, k, dpi, pixels, read, black, white;
+ float coeff;
+ unsigned int red, green, blue;
+ int line;
+ SANE_Status status;
+ char title[40];
+
+ FILE *dbg = fopen ("debug.pnm", "wb");
+ fprintf (dbg, "P6\n%d %d\n255\n", MAX_SENSOR_PIXELS,
+ CALIBRATION_SKIP_LINES * 4);
+
+ DBG (DBG_proc, "sheetfed_calibration: start\n");
+
+ /* check calibration target has been loaded in ADF */
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS)
+ {
+ DBG (DBG_error,
+ "sheetfed_calibration: no calibration target present!\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+
+ /* clean up calibration data */
+ cleanup_calibration (dev);
+
+ /* a RGB scan to get reference data */
+ /* initialize calibration slot for the resolution */
+ i = 0;
+ dpi = dev->model->max_xdpi;
+ pixels = MAX_SENSOR_PIXELS;
+ dev->calibration_data[i] =
+ (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
+ if (dev->calibration_data[i] == NULL)
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: failed to allocate memory for calibration\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->calibration_data[i]->dpi = dpi;
+
+ /* start scan */
+ status = start_scan (dev, MODE_COLOR, dpi, 0, pixels);
+ if (status != SANE_STATUS_GOOD)
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: failed to start scan at %d dpi\n", dpi);
+ return SANE_STATUS_INVAL;
+ }
+
+ white = 0;
+ black = 0;
+ read = 0;
+ for (j = 0; j < pixels * 3; j++)
+ {
+ black_data[j] = 0;
+ white_data[j] = 0;
+ }
+
+ /* read lines and gather black and white ones until enough for sensor's
+ * native resolution */
+ do
+ {
+ status = test_document (dev->fd);
+ if (status == SANE_STATUS_NO_DOCS && (white < 10 || black < 10))
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: calibration sheet too short!\n");
+ return SANE_STATUS_INVAL;
+ }
+ memset (buffer, 0x00, MAX_SENSOR_PIXELS * 3);
+ line =
+ read_line (dev, buffer, pixels * 3, 1, SANE_FALSE, SANE_FALSE,
+ MODE_COLOR, SANE_FALSE);
+ if (line == -1)
+ {
+ DBG (DBG_error, "sheetfed_calibration: failed to read data\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* if a data line has been read, add it to reference data */
+ if (line)
+ {
+ read++;
+ fwrite (buffer, pixels * 3, 1, dbg);
+ if (is_white_line (buffer, pixels, MODE_COLOR) && white < 256)
+ {
+ white++;
+ /* first calibration lines are skipped */
+ for (j = 0; j < pixels * 3 && read > CALIBRATION_SKIP_LINES;
+ j++)
+ {
+ white_data[j] += buffer[j];
+ }
+ }
+ if (is_black_line (buffer, pixels, MODE_COLOR) && black < 256)
+ {
+ black++;
+ for (j = 0; j < pixels * 3; j++)
+ {
+ black_data[j] += buffer[j];
+ }
+ }
+ }
+ }
+ while (test_document (dev->fd) != SANE_STATUS_NO_DOCS);
+ DBG (DBG_trace, "sheetfed_calibration: white lines=%d, black lines=%d\n",
+ white, black);
+
+ /* average pixels and store in per dpi calibration data */
+ for (j = 0; j < pixels * 3; j++)
+ {
+ dev->calibration_data[i]->white_data[j] = white_data[j] / white;
+ dev->calibration_data[i]->black_data[j] = black_data[j] / black;
+ }
+
+ /* we average red, green and blue offset on the full sensor */
+ red = 0;
+ green = 0;
+ blue = 0;
+ for (j = 0; j < pixels * 3; j += 3)
+ {
+ red += dev->calibration_data[i]->black_data[j];
+ green += dev->calibration_data[i]->black_data[j + 1];
+ blue += dev->calibration_data[i]->black_data[j + 2];
+ }
+ for (j = 0; j < pixels * 3; j += 3)
+ {
+ dev->calibration_data[i]->black_data[j] = red / pixels;
+ dev->calibration_data[i]->black_data[j + 1] = green / pixels;
+ dev->calibration_data[i]->black_data[j + 2] = blue / pixels;
+ }
+
+ /* trace calibration data for debug */
+ if (DBG_LEVEL > DBG_data)
+ {
+ sprintf (title, "calibration-white-%d.pnm",
+ dev->calibration_data[i]->dpi);
+ write_rgb_data (title, dev->calibration_data[i]->white_data, pixels, 1);
+ sprintf (title, "calibration-black-%d.pnm",
+ dev->calibration_data[i]->dpi);
+ write_rgb_data (title, dev->calibration_data[i]->black_data, pixels, 1);
+ }
+
+ /* loop on all remaining resolution and compute calibration data from it */
+ for (i = 1; i < MAX_RESOLUTIONS && dev->model->xdpi_values[i] > 0; i++)
+ {
+ dev->calibration_data[i] =
+ (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
+ if (dev->calibration_data[i] == NULL)
+ {
+ cleanup_calibration (dev);
+ DBG (DBG_error,
+ "sheetfed_calibration: failed to allocate memory for calibration\n");
+ return SANE_STATUS_INVAL;
+ }
+ dev->calibration_data[i]->dpi = dev->model->xdpi_values[i];
+
+ coeff = ((float) dev->model->xdpi_values[i]) / (float) dpi;
+
+ /* generate data by decimation */
+ for (j = 0; j < pixels / coeff; j++)
+ {
+ k = j * coeff;
+ dev->calibration_data[i]->white_data[j] =
+ dev->calibration_data[0]->white_data[k];
+ dev->calibration_data[i]->white_data[j + 1] =
+ dev->calibration_data[0]->white_data[k + 1];
+ dev->calibration_data[i]->white_data[j + 2] =
+ dev->calibration_data[0]->white_data[k + 2];
+ dev->calibration_data[i]->black_data[j] =
+ dev->calibration_data[0]->black_data[k];
+ dev->calibration_data[i]->black_data[j + 1] =
+ dev->calibration_data[0]->black_data[k + 1];
+ dev->calibration_data[i]->black_data[j + 2] =
+ dev->calibration_data[0]->black_data[k + 2];
+ }
+ }
+
+ fclose (dbg);
+ dev->calibrated = SANE_TRUE;
+
+ /* eject calibration target */
+ eject (dev->fd);
+
+ DBG (DBG_proc, "sheetfed_calibration: end\n");
+ return SANE_STATUS_GOOD;
+}
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */