summaryrefslogtreecommitdiff
path: root/backend/epsonds-ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/epsonds-ops.c')
-rw-r--r--backend/epsonds-ops.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/backend/epsonds-ops.c b/backend/epsonds-ops.c
new file mode 100644
index 0000000..94f1071
--- /dev/null
+++ b/backend/epsonds-ops.c
@@ -0,0 +1,474 @@
+/*
+ * epsonds-ops.c - Epson ESC/I-2 driver, support routines.
+ *
+ * Copyright (C) 2015 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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, version 2.
+ */
+
+#define DEBUG_DECLARE_ONLY
+
+#include "sane/config.h"
+
+#include <unistd.h> /* sleep */
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include "epsonds.h"
+#include "epsonds-io.h"
+#include "epsonds-ops.h"
+#include "epsonds-cmd.h"
+
+extern struct mode_param mode_params[];
+
+/* Define the different scan sources */
+
+#define FBF_STR SANE_I18N("Flatbed")
+#define TPU_STR SANE_I18N("Transparency Unit")
+#define ADF_STR SANE_I18N("Automatic Document Feeder")
+
+extern SANE_String_Const source_list[];
+
+void
+eds_dev_init(epsonds_device *dev)
+{
+ dev->res_list = malloc(sizeof(SANE_Word));
+ dev->res_list[0] = 0;
+
+ dev->depth_list = malloc(sizeof(SANE_Word));
+ dev->depth_list[0] = 0;
+}
+
+SANE_Status
+eds_dev_post_init(struct epsonds_device *dev)
+{
+ DBG(10, "%s\n", __func__);
+
+ SANE_String_Const *source_list_add = source_list;
+ if (dev->has_fb)
+ *source_list_add++ = FBF_STR;
+
+ if (dev->has_adf)
+ *source_list_add++ = ADF_STR;
+
+ if (source_list[0] == 0
+ || (dev->res_list[0] == 0 && dev->dpi_range.min == 0)
+ || dev->depth_list[0] == 0) {
+
+ DBG(1, "something is wrong in the discovery process, aborting.\n");
+ DBG(1, "sources: %ld, res: %d, depths: %d.\n",
+ source_list_add - source_list, dev->res_list[0], dev->depth_list[0]);
+
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+eds_add_resolution(epsonds_device *dev, int r)
+{
+ DBG(10, "%s: add (dpi): %d\n", __func__, r);
+
+ /* first element is the list size */
+ dev->res_list[0]++;
+ dev->res_list = realloc(dev->res_list,
+ (dev->res_list[0] + 1) *
+ sizeof(SANE_Word));
+ if (dev->res_list == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->res_list[dev->res_list[0]] = r;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+eds_set_resolution_range(epsonds_device *dev, int min, int max)
+{
+ DBG(10, "%s: set min/max (dpi): %d/%d\n", __func__, min, max);
+
+ dev->dpi_range.min = min;
+ dev->dpi_range.max = max;
+ dev->dpi_range.quant = 1;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+eds_set_fbf_area(epsonds_device *dev, int x, int y, int unit)
+{
+ if (x == 0 || y == 0)
+ return;
+
+ dev->fbf_x_range.min = 0;
+ dev->fbf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->fbf_x_range.quant = 0;
+
+ dev->fbf_y_range.min = 0;
+ dev->fbf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->fbf_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->fbf_x_range.min),
+ SANE_UNFIX(dev->fbf_y_range.min),
+ SANE_UNFIX(dev->fbf_x_range.max),
+ SANE_UNFIX(dev->fbf_y_range.max), unit);
+}
+
+void
+eds_set_adf_area(struct epsonds_device *dev, int x, int y, int unit)
+{
+ dev->adf_x_range.min = 0;
+ dev->adf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->adf_x_range.quant = 0;
+
+ dev->adf_y_range.min = 0;
+ dev->adf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->adf_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->adf_x_range.min),
+ SANE_UNFIX(dev->adf_y_range.min),
+ SANE_UNFIX(dev->adf_x_range.max),
+ SANE_UNFIX(dev->adf_y_range.max), unit);
+}
+
+void
+eds_set_tpu_area(struct epsonds_device *dev, int x, int y, int unit)
+{
+ dev->tpu_x_range.min = 0;
+ dev->tpu_x_range.max = SANE_FIX(x * MM_PER_INCH / unit);
+ dev->tpu_x_range.quant = 0;
+
+ dev->tpu_y_range.min = 0;
+ dev->tpu_y_range.max = SANE_FIX(y * MM_PER_INCH / unit);
+ dev->tpu_y_range.quant = 0;
+
+ DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
+ __func__,
+ SANE_UNFIX(dev->tpu_x_range.min),
+ SANE_UNFIX(dev->tpu_y_range.min),
+ SANE_UNFIX(dev->tpu_x_range.max),
+ SANE_UNFIX(dev->tpu_y_range.max), unit);
+}
+
+SANE_Status
+eds_add_depth(epsonds_device *dev, SANE_Word depth)
+{
+ DBG(5, "%s: add (bpp): %d\n", __func__, depth);
+
+ /* > 8bpp not implemented yet */
+ if (depth > 8) {
+ DBG(1, " not supported");
+ return SANE_STATUS_GOOD;
+ }
+
+ if (depth > dev->max_depth)
+ dev->max_depth = depth;
+
+ /* first element is the list size */
+ dev->depth_list[0]++;
+ dev->depth_list = realloc(dev->depth_list,
+ (dev->depth_list[0] + 1) *
+ sizeof(SANE_Word));
+
+ if (dev->depth_list == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev->depth_list[dev->depth_list[0]] = depth;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+eds_init_parameters(epsonds_scanner *s)
+{
+ int dpi, bytes_per_pixel;
+
+ memset(&s->params, 0, sizeof(SANE_Parameters));
+
+ s->dummy = 0;
+
+ dpi = s->val[OPT_RESOLUTION].w;
+
+ if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 ||
+ SANE_UNFIX(s->val[OPT_BR_X].w) == 0)
+ return SANE_STATUS_INVAL;
+
+ s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) *
+ s->val[OPT_RESOLUTION].w) + 0.5;
+
+ s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) *
+ s->val[OPT_RESOLUTION].w) + 0.5;
+
+ s->params.pixels_per_line =
+ ((SANE_UNFIX(s->val[OPT_BR_X].w -
+ s->val[OPT_TL_X].w) / MM_PER_INCH) * dpi) + 0.5;
+ s->params.lines =
+ ((SANE_UNFIX(s->val[OPT_BR_Y].w -
+ s->val[OPT_TL_Y].w) / MM_PER_INCH) * dpi) + 0.5;
+
+ DBG(5, "%s: tlx %f tly %f brx %f bry %f [mm]\n",
+ __func__,
+ SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w),
+ SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w));
+
+ DBG(5, "%s: tlx %d tly %d brx %d bry %d [dots @ %d dpi]\n",
+ __func__, s->left, s->top,
+ s->params.pixels_per_line, s->params.lines, dpi);
+
+ /* center aligned? */
+ if (s->hw->alignment == 1) {
+
+ SANE_Int offset = ((SANE_UNFIX(s->hw->x_range->max) / MM_PER_INCH) * dpi) + 0.5;
+
+ s->left += ((offset - s->params.pixels_per_line) / 2);
+
+ DBG(5, "%s: centered to tlx %d tly %d brx %d bry %d [dots @ %d dpi]\n",
+ __func__, s->left, s->top,
+ s->params.pixels_per_line, s->params.lines, dpi);
+ }
+
+ /*
+ * Calculate bytes_per_pixel and bytes_per_line for
+ * any color depths.
+ *
+ * The default color depth is stored in mode_params.depth:
+ */
+
+ if (mode_params[s->val[OPT_MODE].w].depth == 1)
+ s->params.depth = 1;
+ else
+ s->params.depth = s->val[OPT_DEPTH].w;
+
+ /* this works because it can only be set to 1, 8 or 16 */
+ bytes_per_pixel = s->params.depth / 8;
+ if (s->params.depth % 8) { /* just in case ... */
+ bytes_per_pixel++;
+ }
+
+ /* pixels_per_line is rounded to the next 8bit boundary */
+ s->params.pixels_per_line = s->params.pixels_per_line & ~7;
+
+ s->params.last_frame = SANE_TRUE;
+
+ switch (s->val[OPT_MODE].w) {
+ case MODE_BINARY:
+ case MODE_GRAY:
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.bytes_per_line =
+ s->params.pixels_per_line * s->params.depth / 8;
+ break;
+ case MODE_COLOR:
+ s->params.format = SANE_FRAME_RGB;
+ s->params.bytes_per_line =
+ 3 * s->params.pixels_per_line * bytes_per_pixel;
+ break;
+ }
+
+ if (s->params.bytes_per_line == 0) {
+ DBG(1, "bytes_per_line is ZERO\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /*
+ * If (s->top + s->params.lines) is larger than the max scan area, reset
+ * the number of scan lines:
+ * XXX: precalculate the maximum scanning area elsewhere (use dev max_y)
+ */
+
+ if (SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH * dpi <
+ (s->params.lines + s->top)) {
+ s->params.lines =
+ ((int) SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH *
+ dpi + 0.5) - s->top;
+ }
+
+ if (s->params.lines <= 0) {
+ DBG(1, "wrong number of lines: %d\n", s->params.lines);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+eds_copy_image_from_ring(epsonds_scanner *s, SANE_Byte *data, SANE_Int max_length,
+ SANE_Int *length)
+{
+ int lines, available;
+ int hw_line_size = (s->params.bytes_per_line + s->dummy);
+
+ /* trim max_length to a multiple of hw_line_size */
+ max_length -= (max_length % hw_line_size);
+
+ /* check available data */
+ available = eds_ring_avail(s->current);
+ if (max_length > available)
+ max_length = available;
+
+ lines = max_length / hw_line_size;
+
+ DBG(18, "copying %d lines (%d, %d)\n", lines, s->params.bytes_per_line, s->dummy);
+
+ /* need more data? */
+ if (lines == 0) {
+ *length = 0;
+ return;
+ }
+
+ *length = (lines * s->params.bytes_per_line);
+
+ /* we need to copy one line at time, skipping
+ * dummy bytes at the end of each line
+ */
+
+ /* lineart */
+ if (s->params.depth == 1) {
+
+ while (lines--) {
+
+ eds_ring_read(s->current, s->line_buffer, s->params.bytes_per_line);
+ eds_ring_skip(s->current, s->dummy);
+
+ int i;
+
+ SANE_Byte *p = s->line_buffer;
+
+ for (i = 0; i < s->params.bytes_per_line; i++) {
+ *data++ = ~*p++;
+ }
+ }
+
+ } else { /* gray and color */
+
+ while (lines--) {
+
+ eds_ring_read(s->current, data, s->params.bytes_per_line);
+ eds_ring_skip(s->current, s->dummy);
+
+ data += s->params.bytes_per_line;
+
+ }
+ }
+}
+
+SANE_Status eds_ring_init(ring_buffer *ring, SANE_Int size)
+{
+ ring->ring = realloc(ring->ring, size);
+ if (!ring->ring) {
+ return SANE_STATUS_NO_MEM;
+ }
+
+ ring->size = size;
+ ring->fill = 0;
+ ring->end = ring->ring + size;
+ ring->wp = ring->rp = ring->ring;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status eds_ring_write(ring_buffer *ring, SANE_Byte *buf, SANE_Int size)
+{
+ if (size > (ring->size - ring->fill)) {
+ DBG(1, "ring buffer full, requested: %d, available: %d\n", size, ring->size - ring->fill);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ SANE_Int tail = ring->end - ring->wp;
+ if (size < tail) {
+
+ memcpy(ring->wp, buf, size);
+
+ ring->wp += size;
+ ring->fill += size;
+
+ } else {
+
+ memcpy(ring->wp, buf, tail);
+ size -= tail;
+
+ ring->wp = ring->ring;
+ memcpy(ring->wp, buf + tail, size);
+
+ ring->wp += size;
+ ring->fill += (tail + size);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Int eds_ring_read(ring_buffer *ring, SANE_Byte *buf, SANE_Int size)
+{
+ DBG(18, "reading from ring, %d bytes available\n", (int)ring->fill);
+
+ /* limit read to available */
+ if (size > ring->fill) {
+ DBG(1, "not enough data in the ring, shouldn't happen\n");
+ size = ring->fill;
+ }
+
+ SANE_Int tail = ring->end - ring->rp;
+ if (size < tail) {
+
+ memcpy(buf, ring->rp, size);
+
+ ring->rp += size;
+ ring->fill -= size;
+
+ return size;
+
+ } else {
+
+ memcpy(buf, ring->rp, tail);
+ size -= tail;
+
+ ring->rp = ring->ring;
+ memcpy(buf + tail, ring->rp, size);
+
+ ring->rp += size;
+ ring->fill -= (size + tail);
+
+ return size + tail;
+ }
+}
+
+SANE_Int eds_ring_skip(ring_buffer *ring, SANE_Int size)
+{
+ /* limit skip to available */
+ if (size > ring->fill)
+ size = ring->fill;
+
+ SANE_Int tail = ring->end - ring->rp;
+ if (size < tail) {
+ ring->rp += size;
+ } else {
+
+ ring->rp = ring->ring + (size - tail);
+ }
+
+ ring->fill -= size;
+
+ return size;
+}
+
+SANE_Int eds_ring_avail(ring_buffer *ring)
+{
+ return ring->fill;
+}
+
+void eds_ring_flush(ring_buffer *ring)
+{
+ eds_ring_skip(ring, ring->fill);
+}
+
+