kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			493 wiersze
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			493 wiersze
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
| /*
 | |
|  * 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)
 | |
| {
 | |
| 	SANE_String_Const *source_list_add = source_list;
 | |
| 
 | |
| 	DBG(10, "%s\n", __func__);
 | |
| 
 | |
| 	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_Bool
 | |
| eds_is_model(epsonds_device *dev, const char *model)
 | |
| {
 | |
|         if (dev->model == NULL)
 | |
|                 return SANE_FALSE;
 | |
| 
 | |
|         if (strncmp(dev->model, model, strlen(model)) == 0)
 | |
|                 return SANE_TRUE;
 | |
| 
 | |
|         return SANE_FALSE;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 
 | |
| 	/* setup depth according to our mode table */
 | |
| 	if (mode_params[s->val[OPT_MODE].w].depth == 1)
 | |
| 		s->params.depth = 1;
 | |
| 	else
 | |
| 		s->params.depth = s->val[OPT_DEPTH].w;
 | |
| 
 | |
| 	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:
 | |
| 	 */
 | |
| 
 | |
| 	/* 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--) {
 | |
| 
 | |
| 			int i;
 | |
| 			SANE_Byte *p;
 | |
| 
 | |
| 			eds_ring_read(s->current, s->line_buffer, s->params.bytes_per_line);
 | |
| 			eds_ring_skip(s->current, s->dummy);
 | |
| 
 | |
| 			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)
 | |
| {
 | |
| 	SANE_Int tail;
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	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)
 | |
| {
 | |
| 	SANE_Int tail;
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	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)
 | |
| {
 | |
| 	SANE_Int tail;
 | |
| 	/* limit skip to available */
 | |
| 	if (size > ring->fill)
 | |
| 		size = ring->fill;
 | |
| 
 | |
| 	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);
 | |
| }
 |