sane-project-backends/backend/epson2.c

2611 wiersze
67 KiB
C
Czysty Zwykły widok Historia

2006-12-02 14:47:21 +00:00
/*
* epson2.c - SANE library for Epson scanners.
*
* Based on Kazuhiro Sasayama previous
* Work on epson.[ch] file from the SANE package.
* Please see those files for additional copyrights.
2006-12-02 14:47:21 +00:00
*
* Copyright (C) 2006-07 Tower Technologies
2006-12-02 14:47:21 +00:00
* 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 EPSON2_VERSION 1
#define EPSON2_REVISION 0
#define EPSON2_BUILD 122
2006-12-02 14:47:21 +00:00
/* debugging levels:
*
* 127 e2_recv buffer
* 125 e2_send buffer
* 32 more network progression
* 24 network header
* 23 network info
2006-12-02 14:47:21 +00:00
* 20 usb cmd counters
* 18 sane_read
* 17 setvalue, getvalue, control_option
* 15 e2_send, e2_recv calls
* 13 e2_cmd_info_block
2006-12-02 14:47:21 +00:00
* 12 epson_cmd_simple
* 11 even more
* 10 more debug in ESC/I commands
* 9 ESC x/FS x in e2_send
* 8 ESC/I commands
* 7 open/close/attach
* 6 print_params
* 5 basic functions
2006-12-02 14:47:21 +00:00
* 3 status information
* 1 scanner info and capabilities
* warnings
*/
#include "sane/config.h"
2006-12-02 14:47:21 +00:00
#include "epson2.h"
2006-12-02 14:47:21 +00:00
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
2008-02-02 21:18:13 +00:00
#include <sys/time.h>
2006-12-02 14:47:21 +00:00
#include "sane/saneopts.h"
#include "sane/sanei_scsi.h"
#include "sane/sanei_usb.h"
#include "sane/sanei_pio.h"
#include "sane/sanei_tcp.h"
#include "sane/sanei_udp.h"
#include "sane/sanei_backend.h"
#include "sane/sanei_config.h"
2006-12-02 14:47:21 +00:00
#include "epson2-io.h"
#include "epson2-commands.h"
#include "epson2-ops.h"
2006-12-02 14:47:21 +00:00
#include "epson2_scsi.h"
#include "epson_usb.h"
#include "epson2_net.h"
/*
* Definition of the mode_param struct, that is used to
* specify the valid parameters for the different scan modes.
*
* The depth variable gets updated when the bit depth is modified.
*/
struct mode_param mode_params[] = {
2006-12-02 14:47:21 +00:00
{0, 0x00, 0x30, 1},
{0, 0x00, 0x30, 8},
{1, 0x02, 0x00, 8}
};
static const SANE_String_Const mode_list[] = {
"Binary",
"Gray",
"Color",
2006-12-02 14:47:21 +00:00
NULL
};
static const SANE_String_Const adf_mode_list[] = {
"Simplex",
"Duplex",
2006-12-02 14:47:21 +00:00
NULL
};
/*
* source list need one dummy entry (save device settings is crashing).
* NOTE: no const - this list gets created while exploring the capabilities
* of the scanner.
*/
SANE_String_Const source_list[] = {
2006-12-02 14:47:21 +00:00
FBF_STR,
NULL,
NULL,
NULL
};
static const SANE_String_Const film_list[] = {
"Positive Film",
"Negative Film",
"Positive Slide",
"Negative Slide",
2006-12-02 14:47:21 +00:00
NULL
};
static const SANE_String_Const focus_list[] = {
"Focus on glass",
"Focus 2.5mm above glass",
2006-12-02 14:47:21 +00:00
NULL
};
#define HALFTONE_NONE 0x01
#define HALFTONE_TET 0x03
const int halftone_params[] = {
2006-12-02 14:47:21 +00:00
HALFTONE_NONE,
0x00,
0x10,
0x20,
0x80,
0x90,
0xa0,
0xb0,
HALFTONE_TET,
0xc0,
0xd0
};
static const SANE_String_Const halftone_list[] = {
"None",
"Halftone A (Hard Tone)",
"Halftone B (Soft Tone)",
"Halftone C (Net Screen)",
2006-12-02 14:47:21 +00:00
NULL
};
static const SANE_String_Const halftone_list_4[] = {
"None",
"Halftone A (Hard Tone)",
"Halftone B (Soft Tone)",
"Halftone C (Net Screen)",
"Dither A (4x4 Bayer)",
"Dither B (4x4 Spiral)",
"Dither C (4x4 Net Screen)",
"Dither D (8x4 Net Screen)",
2006-12-02 14:47:21 +00:00
NULL
};
static const SANE_String_Const halftone_list_7[] = {
"None",
"Halftone A (Hard Tone)",
"Halftone B (Soft Tone)",
"Halftone C (Net Screen)",
"Dither A (4x4 Bayer)",
"Dither B (4x4 Spiral)",
"Dither C (4x4 Net Screen)",
"Dither D (8x4 Net Screen)",
"Text Enhanced Technology",
"Download pattern A",
"Download pattern B",
2006-12-02 14:47:21 +00:00
NULL
};
static const SANE_String_Const dropout_list[] = {
"None",
"Red",
"Green",
"Blue",
2006-12-02 14:47:21 +00:00
NULL
};
static const SANE_Bool color_userdefined[] = {
SANE_FALSE,
SANE_TRUE,
SANE_FALSE,
SANE_FALSE,
SANE_FALSE,
SANE_FALSE
};
static const SANE_String_Const color_list[] = {
"No Correction",
"User defined",
"Impact-dot printers",
"Thermal printers",
"Ink-jet printers",
"CRT monitors",
2006-12-02 14:47:21 +00:00
NULL
};
/*
* Gamma correction:
* The A and B level scanners work differently than the D level scanners,
* therefore I define two different sets of arrays, plus one set of
* variables that get set to the actally used params and list arrays at runtime.
2006-12-02 14:47:21 +00:00
*/
2006-12-02 14:47:21 +00:00
static int gamma_params_ab[] = {
0x01,
0x03,
0x00,
0x10,
0x20
};
static const SANE_String_Const gamma_list_ab[] = {
"Default",
"User defined",
"High density printing",
"Low density printing",
"High contrast printing",
2006-12-02 14:47:21 +00:00
NULL
};
static SANE_Bool gamma_userdefined_ab[] = {
SANE_FALSE,
SANE_TRUE,
SANE_FALSE,
SANE_FALSE,
SANE_FALSE,
};
static int gamma_params_d[] = {
0x03,
0x04
};
static const SANE_String_Const gamma_list_d[] = {
"User defined (Gamma=1.0)",
"User defined (Gamma=1.8)",
2006-12-02 14:47:21 +00:00
NULL
};
static SANE_Bool gamma_userdefined_d[] = {
SANE_TRUE,
SANE_TRUE
};
static SANE_Bool *gamma_userdefined;
int *gamma_params;
2006-12-02 14:47:21 +00:00
/* Bay list:
* this is used for the FilmScan
* XXX Add APS loader support
*/
static const SANE_String_Const bay_list[] = {
"1",
"2",
"3",
"4",
"5",
"6",
2006-12-02 14:47:21 +00:00
NULL
};
/* minimum, maximum, quantization */
static const SANE_Range u8_range = { 0, 255, 0 };
static const SANE_Range s8_range = { -127, 127, 0 };
static const SANE_Range outline_emphasis_range = { -2, 2, 0 };
/*
* List of pointers to devices - will be dynamically allocated depending
* on the number of devices found.
*/
static const SANE_Device **devlist;
2006-12-02 14:47:21 +00:00
/* Some utility functions */
static size_t
max_string_size(const SANE_String_Const strings[])
{
size_t size, max_size = 0;
int i;
for (i = 0; strings[i]; i++) {
size = strlen(strings[i]) + 1;
if (size > max_size)
max_size = size;
}
return max_size;
}
static SANE_Status attach_one_usb(SANE_String_Const devname);
static SANE_Status attach_one_net(SANE_String_Const devname);
static void filter_resolution_list(Epson_Scanner *s);
2006-12-02 14:47:21 +00:00
static void
print_params(const SANE_Parameters params)
{
DBG(6, "params.format = %d\n", params.format);
DBG(6, "params.last_frame = %d\n", params.last_frame);
DBG(6, "params.bytes_per_line = %d\n", params.bytes_per_line);
DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line);
DBG(6, "params.lines = %d\n", params.lines);
DBG(6, "params.depth = %d\n", params.depth);
2006-12-02 14:47:21 +00:00
}
/*
* close_scanner()
*
* Close the open scanner. Depending on the connection method, a different
* close function is called.
*/
2006-12-02 14:47:21 +00:00
static void
close_scanner(Epson_Scanner *s)
2006-12-02 14:47:21 +00:00
{
DBG(7, "%s: fd = %d\n", __func__, s->fd);
2006-12-02 14:47:21 +00:00
if (s->fd == -1)
return;
2006-12-02 14:47:21 +00:00
/* send a request_status. This toggles w_cmd_count and r_cmd_count */
if (r_cmd_count % 2)
esci_request_status(s, NULL);
2006-12-02 14:47:21 +00:00
/* request extended status. This toggles w_cmd_count only */
if (w_cmd_count % 2)
esci_request_extended_status(s, NULL, NULL);
2006-12-02 14:47:21 +00:00
if (s->hw->connection == SANE_EPSON_NET) {
sanei_epson_net_unlock(s);
sanei_tcp_close(s->fd);
} else if (s->hw->connection == SANE_EPSON_SCSI) {
sanei_scsi_close(s->fd);
} else if (s->hw->connection == SANE_EPSON_PIO) {
sanei_pio_close(s->fd);
} else if (s->hw->connection == SANE_EPSON_USB) {
sanei_usb_close(s->fd);
}
2006-12-02 14:47:21 +00:00
s->fd = -1;
2006-12-02 14:47:21 +00:00
}
static void
e2_network_discovery(void)
{
fd_set rfds;
int fd, len;
SANE_Status status;
char *ip, *query = "EPSONP\x00\xff\x00\x00\x00\x00\x00\x00\x00";
unsigned char buf[76];
struct timeval to;
long save_flags, flags;
status = sanei_udp_open_broadcast(&fd);
if (status != SANE_STATUS_GOOD)
return;
sanei_udp_write_broadcast(fd, 3289, (unsigned char *) query, 15);
DBG(5, "%s, sent discovery packet\n", __func__);
to.tv_sec = 1;
to.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
save_flags = flags = fcntl(fd, F_GETFL, 0L);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
if (select(fd + 1, &rfds, NULL, NULL, &to) > 0) {
while ((len = sanei_udp_recvfrom(fd, buf, 76, &ip)) == 76) {
DBG(5, " response from %s\n", ip);
/* minimal check, protocol unknown */
if (strncmp((char *) buf, "EPSON", 5) == 0)
attach_one_net(ip);
}
}
fcntl(fd, F_SETFL, save_flags);
DBG(5, "%s, end\n", __func__);
sanei_udp_close(fd);
}
2006-12-02 14:47:21 +00:00
/*
* open_scanner()
*
* Open the scanner device. Depending on the connection method,
* different open functions are called.
2006-12-02 14:47:21 +00:00
*/
static SANE_Status
open_scanner(Epson_Scanner *s)
2006-12-02 14:47:21 +00:00
{
SANE_Status status = 0;
2006-12-02 14:47:21 +00:00
DBG(7, "%s: %s\n", __func__, s->hw->sane.name);
2006-12-02 14:47:21 +00:00
if (s->fd != -1) {
DBG(5, "scanner is already open: fd = %d\n", s->fd);
return SANE_STATUS_GOOD; /* no need to open the scanner */
2006-12-02 14:47:21 +00:00
}
if (s->hw->connection == SANE_EPSON_NET) {
unsigned char buf[5];
2006-12-02 14:47:21 +00:00
/* Sleep a bit or the network scanner will not be ready */
sleep(2);
status = sanei_tcp_open(s->hw->sane.name, 1865, &s->fd);
if (status == SANE_STATUS_GOOD) {
ssize_t read;
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
s->netlen = 0;
2006-12-02 14:47:21 +00:00
DBG(32, "awaiting welcome message\n");
/* the scanner sends a kind of welcome msg */
read = e2_recv(s, buf, 5, &status);
if (read != 5)
return SANE_STATUS_IO_ERROR;
DBG(32, "welcome message received, locking the scanner...\n");
2006-12-02 14:47:21 +00:00
/* lock the scanner for use by sane */
sanei_epson_net_lock(s);
DBG(32, "scanner locked\n");
}
} else if (s->hw->connection == SANE_EPSON_SCSI)
status = sanei_scsi_open(s->hw->sane.name, &s->fd,
sanei_epson2_scsi_sense_handler,
NULL);
else if (s->hw->connection == SANE_EPSON_PIO)
status = sanei_pio_open(s->hw->sane.name, &s->fd);
else if (s->hw->connection == SANE_EPSON_USB)
status = sanei_usb_open(s->hw->sane.name, &s->fd);
2006-12-02 14:47:21 +00:00
if (status != SANE_STATUS_GOOD)
DBG(1, "%s open failed: %s\n", s->hw->sane.name,
sane_strstatus(status));
2006-12-02 14:47:21 +00:00
if (status == SANE_STATUS_ACCESS_DENIED) {
DBG(1, "please check that you have permissions on the device.\n");
DBG(1, "if this is a multi-function device with a printer,\n");
DBG(1, "disable any conflicting driver (like usblp).\n");
}
DBG(5, "scanner opened\n");
2006-12-02 14:47:21 +00:00
return status;
}
static SANE_Status detect_scsi(struct Epson_Scanner *s)
{
SANE_Status status;
struct Epson_Device *dev = s->hw;
char buf[INQUIRY_BUF_SIZE + 1];
size_t buf_size = INQUIRY_BUF_SIZE;
char *vendor = buf + 8;
char *model = buf + 16;
char *rev = buf + 32;
status = sanei_epson2_scsi_inquiry(s->fd, buf, &buf_size);
if (status != SANE_STATUS_GOOD) {
DBG(1, "%s: inquiry failed: %s\n", __func__,
sane_strstatus(status));
return status;
}
buf[INQUIRY_BUF_SIZE] = 0;
DBG(1, "inquiry data:\n");
DBG(1, " vendor : %.8s\n", vendor);
DBG(1, " model : %.16s\n", model);
DBG(1, " revision: %.4s\n", rev);
if (buf[0] != TYPE_PROCESSOR) {
DBG(1, "%s: device is not of processor type (%d)\n",
__func__, buf[0]);
return SANE_STATUS_INVAL;
}
if (strncmp(vendor, "EPSON", 5) != 0) {
DBG(1,
"%s: device doesn't look like an EPSON scanner\n",
__func__);
return SANE_STATUS_INVAL;
}
if (strncmp(model, "SCANNER ", 8) != 0
&& strncmp(model, "FilmScan 200", 12) != 0
&& strncmp(model, "Perfection", 10) != 0
&& strncmp(model, "Expression", 10) != 0
&& strncmp(model, "GT", 2) != 0) {
DBG(1, "%s: this EPSON scanner is not supported\n",
__func__);
return SANE_STATUS_INVAL;
}
if (strncmp(model, "FilmScan 200", 12) == 0) {
dev->sane.type = "film scanner";
e2_set_model(s, (unsigned char *) model, 12);
}
/* Issue a test unit ready SCSI command. The FilmScan 200
* requires it for a sort of "wake up". We might eventually
* get the return code and reissue it in case of failure.
*/
sanei_epson2_scsi_test_unit_ready(s->fd);
return SANE_STATUS_GOOD;
}
static SANE_Status
detect_usb(struct Epson_Scanner *s)
{
SANE_Status status;
int vendor, product;
int i, numIds;
SANE_Bool is_valid;
/* if the sanei_usb_get_vendor_product call is not supported,
* then we just ignore this and rely on the user to config
* the correct device.
*/
status = sanei_usb_get_vendor_product(s->fd, &vendor, &product);
if (status != SANE_STATUS_GOOD) {
DBG(1,
"cannot use IOCTL interface to verify that device is a scanner - will continue\n");
return SANE_STATUS_GOOD;
}
/* check the vendor ID to see if we are dealing with an EPSON device */
if (vendor != SANE_EPSON_VENDOR_ID) {
/* this is not a supported vendor ID */
DBG(1,
"the device at %s is not manufactured by EPSON (vendor id=0x%x)\n",
s->hw->sane.name, vendor);
return SANE_STATUS_INVAL;
}
numIds = sanei_epson_getNumberOfUSBProductIds();
is_valid = SANE_FALSE;
i = 0;
/* check all known product IDs to verify that we know
about the device */
while (i != numIds && !is_valid) {
if (product == sanei_epson_usb_product_ids[i])
is_valid = SANE_TRUE;
i++;
}
if (is_valid == SANE_FALSE) {
DBG(1,
"the device at %s is not a supported EPSON scanner (product id=0x%x)\n",
s->hw->sane.name, product);
return SANE_STATUS_INVAL;
}
DBG(1,
"found valid EPSON scanner: 0x%x/0x%x (vendorID/productID)\n",
vendor, product);
return SANE_STATUS_GOOD;
}
2006-12-02 14:47:21 +00:00
static int num_devices; /* number of scanners attached to backend */
static Epson_Device *first_dev; /* first EPSON scanner in list */
2006-12-02 14:47:21 +00:00
/* attach device to backend */
static SANE_Status
attach(const char *name, Epson_Device **devp, int type)
2006-12-02 14:47:21 +00:00
{
SANE_Status status;
Epson_Scanner *s;
struct Epson_Device *dev;
int port;
2006-12-02 14:47:21 +00:00
DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type);
2006-12-02 14:47:21 +00:00
for (dev = first_dev; dev; dev = dev->next) {
if (strcmp(dev->sane.name, name) == 0) {
if (devp) {
*devp = dev;
}
return SANE_STATUS_GOOD;
}
}
2006-12-02 14:47:21 +00:00
/* alloc and clear our device structure */
dev = malloc(sizeof(*dev));
if (!dev) {
DBG(1, "out of memory (line %d)\n", __LINE__);
return SANE_STATUS_NO_MEM;
}
memset(dev, 0x00, sizeof(struct Epson_Device));
/* check for PIO devices */
/* can we convert the device name to an integer? This is only possible
with PIO devices */
if (type != SANE_EPSON_NET) {
port = atoi(name);
if (port != 0)
type = SANE_EPSON_PIO;
}
2006-12-02 14:47:21 +00:00
if (strncmp
(name, SANE_EPSON_CONFIG_PIO,
strlen(SANE_EPSON_CONFIG_PIO)) == 0) {
/* we have a match for the PIO string and adjust the device name */
name += strlen(SANE_EPSON_CONFIG_PIO);
name = sanei_config_skip_whitespace(name);
type = SANE_EPSON_PIO;
}
2006-12-02 14:47:21 +00:00
s = malloc(sizeof(struct Epson_Scanner));
if (s == NULL)
return SANE_STATUS_NO_MEM;
2006-12-02 14:47:21 +00:00
memset(s, 0x00, sizeof(struct Epson_Scanner));
2006-12-02 14:47:21 +00:00
s->fd = -1;
s->hw = dev;
2006-12-02 14:47:21 +00:00
e2_dev_init(dev, name, type);
status = open_scanner(s);
if (status != SANE_STATUS_GOOD) {
free(s);
return status;
}
/* from now on, close_scanner() must be called */
2006-12-02 14:47:21 +00:00
/* SCSI and USB requires special care */
if (dev->connection == SANE_EPSON_SCSI) {
2006-12-02 14:47:21 +00:00
status = detect_scsi(s);
} else if (dev->connection == SANE_EPSON_USB) {
2006-12-02 14:47:21 +00:00
status = detect_usb(s);
}
2006-12-02 14:47:21 +00:00
if (status != SANE_STATUS_GOOD) {
close_scanner(s);
free(s);
return status;
}
2006-12-02 14:47:21 +00:00
/* set name and model (if not already set) */
if (dev->model == NULL)
e2_set_model(s, (unsigned char *) "generic", 7);
dev->name = strdup(name);
dev->sane.name = dev->name;
2006-12-02 14:47:21 +00:00
/* ESC @, reset */
status = esci_reset(s);
if (status != SANE_STATUS_GOOD)
goto free;
status = e2_discover_capabilities(s);
if (status != SANE_STATUS_GOOD)
goto free;
2006-12-02 14:47:21 +00:00
if (source_list[0] == NULL || dev->dpi_range.min == 0) {
DBG(1, "something is wrong in the discovery process, aborting.\n");
status = SANE_STATUS_IO_ERROR;
goto free;
}
/* If we have been unable to obtain supported resolutions
* due to the fact we are on the network transport,
* add some convenient ones
*/
2006-12-02 14:47:21 +00:00
if (dev->res_list_size == 0 && dev->connection == SANE_EPSON_NET) {
2006-12-02 14:47:21 +00:00
int val = (dev->dpi_range.min < 150) ? 150 : dev->dpi_range.min;
DBG(1, "networked scanner, faking resolution list (%d-%d)\n",
dev->dpi_range.min, dev->dpi_range.max);
2006-12-02 14:47:21 +00:00
if (dev->dpi_range.min <= 50)
e2_add_resolution(s, 50);
if (dev->dpi_range.min <= 75)
e2_add_resolution(s, 75);
2006-12-02 14:47:21 +00:00
if (dev->dpi_range.min <= 100)
e2_add_resolution(s, 100);
2006-12-02 14:47:21 +00:00
while (val <= dev->dpi_range.max) {
e2_add_resolution(s, val);
val *= 2;
}
}
2006-12-02 14:47:21 +00:00
/*
* Copy the resolution list to the resolution_list array so that the frontend can
* display the correct values
*/
2006-12-02 14:47:21 +00:00
dev->resolution_list =
malloc((dev->res_list_size + 1) * sizeof(SANE_Word));
2006-12-02 14:47:21 +00:00
if (dev->resolution_list == NULL) {
status = SANE_STATUS_NO_MEM;
goto free;
}
2006-12-02 14:47:21 +00:00
*(dev->resolution_list) = dev->res_list_size;
memcpy(&(dev->resolution_list[1]), dev->res_list,
dev->res_list_size * sizeof(SANE_Word));
2006-12-02 14:47:21 +00:00
status = esci_reset(s);
if (status != SANE_STATUS_GOOD)
goto free;
2006-12-02 14:47:21 +00:00
DBG(1, "scanner model: %s\n", dev->model);
2006-12-02 14:47:21 +00:00
/* establish defaults */
dev->need_reset_on_source_change = SANE_FALSE;
2006-12-02 14:47:21 +00:00
if (e2_model(s, "ES-9000H") || e2_model(s, "GT-30000")) {
dev->cmd->set_focus_position = 0;
dev->cmd->feed = 0x19;
2006-12-02 14:47:21 +00:00
}
if (e2_model(s, "GT-8200") || e2_model(s, "Perfection1650")
|| e2_model(s, "Perfection1640") || e2_model(s, "GT-8700")) {
dev->cmd->feed = 0;
dev->cmd->set_focus_position = 0;
dev->need_reset_on_source_change = SANE_TRUE;
}
2006-12-02 14:47:21 +00:00
/* we are done with this one, prepare for the next scanner */
num_devices++;
dev->next = first_dev;
first_dev = dev;
2006-12-02 14:47:21 +00:00
if (devp)
*devp = dev;
2006-12-02 14:47:21 +00:00
free:
close_scanner(s);
free(s);
return status;
}
2006-12-02 14:47:21 +00:00
/*
* Part of the SANE API: Attaches the scanner with the device name in *dev.
*/
2006-12-02 14:47:21 +00:00
static SANE_Status
attach_one_scsi(const char *dev)
{
DBG(7, "%s: dev = %s\n", __func__, dev);
return attach(dev, 0, SANE_EPSON_SCSI);
}
2006-12-02 14:47:21 +00:00
SANE_Status
attach_one_usb(const char *dev)
{
DBG(7, "%s: dev = %s\n", __func__, dev);
return attach(dev, 0, SANE_EPSON_USB);
}
2006-12-02 14:47:21 +00:00
static SANE_Status
attach_one_net(const char *dev)
{
DBG(7, "%s: dev = %s\n", __func__, dev);
return attach(dev, 0, SANE_EPSON_NET);
}
2006-12-02 14:47:21 +00:00
static SANE_Status
attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
{
int vendor, product;
2006-12-02 14:47:21 +00:00
int len = strlen(line);
DBG(7, "%s: len = %d, line = %s\n", __func__, len, line);
if (sscanf(line, "usb %i %i", &vendor, &product) == 2) {
/* add the vendor and product IDs to the list of
known devices before we call the attach function */
2006-12-02 14:47:21 +00:00
int numIds = sanei_epson_getNumberOfUSBProductIds();
2006-12-02 14:47:21 +00:00
if (vendor != 0x4b8)
return SANE_STATUS_INVAL; /* this is not an EPSON device */
2006-12-02 14:47:21 +00:00
sanei_epson_usb_product_ids[numIds - 1] = product;
sanei_usb_attach_matching_devices(line, attach_one_usb);
2006-12-02 14:47:21 +00:00
} else if (strncmp(line, "usb", 3) == 0 && len == 3) {
int i, numIds;
numIds = sanei_epson_getNumberOfUSBProductIds();
for (i = 0; i < numIds; i++) {
sanei_usb_find_devices(0x4b8,
sanei_epson_usb_product_ids[i], attach_one_usb);
}
} else if (strncmp(line, "net", 3) == 0) {
/* remove the "net" sub string */
const char *name = sanei_config_skip_whitespace(line + 3);
if (strncmp(name, "autodiscovery", 13) == 0)
e2_network_discovery();
else
attach_one_net(name);
} else {
sanei_config_attach_matching_devices(line, attach_one_scsi);
}
return SANE_STATUS_GOOD;
}
static void
free_devices(void)
{
Epson_Device *dev, *next;
DBG(5, "%s\n", __func__);
for (dev = first_dev; dev; dev = next) {
next = dev->next;
free(dev->name);
free(dev->model);
free(dev);
}
free(devlist);
first_dev = NULL;
}
static void
probe_devices(void)
{
DBG(5, "%s\n", __func__);
free_devices();
sanei_configure_attach(EPSON2_CONFIG_FILE, NULL,
attach_one_config);
}
SANE_Status
sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
{
DBG_INIT();
DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__);
DBG(1, "epson2 backend, version %i.%i.%i\n",
EPSON2_VERSION, EPSON2_REVISION, EPSON2_BUILD);
if (version_code != NULL)
*version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR,
EPSON2_BUILD);
sanei_usb_init();
return SANE_STATUS_GOOD;
}
/* Clean up the list of attached scanners. */
void
sane_exit(void)
{
DBG(5, "%s\n", __func__);
free_devices();
}
SANE_Status
sane_get_devices(const SANE_Device ***device_list, SANE_Bool __sane_unused__ local_only)
{
Epson_Device *dev;
int i;
2006-12-02 14:47:21 +00:00
DBG(5, "%s\n", __func__);
2006-12-02 14:47:21 +00:00
probe_devices();
2006-12-02 14:47:21 +00:00
devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
if (!devlist) {
DBG(1, "out of memory (line %d)\n", __LINE__);
return SANE_STATUS_NO_MEM;
2006-12-02 14:47:21 +00:00
}
DBG(5, "%s - results:\n", __func__);
for (i = 0, dev = first_dev; i < num_devices; dev = dev->next, i++) {
DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model);
devlist[i] = &dev->sane;
}
devlist[i] = NULL;
*device_list = devlist;
return SANE_STATUS_GOOD;
}
static SANE_Status
init_options(Epson_Scanner *s)
{
int i;
for (i = 0; i < NUM_OPTIONS; ++i) {
s->opt[i].size = sizeof(SANE_Word);
s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
}
s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
/* "Scan Mode" group: */
s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode");
s->opt[OPT_MODE_GROUP].desc = "";
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_MODE_GROUP].cap = 0;
/* scan mode */
s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
s->opt[OPT_MODE].type = SANE_TYPE_STRING;
s->opt[OPT_MODE].size = max_string_size(mode_list);
s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_MODE].constraint.string_list = mode_list;
s->val[OPT_MODE].w = 0; /* Binary */
/* bit depth */
s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE;
s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->depth_list;
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
s->val[OPT_BIT_DEPTH].w = s->hw->depth_list[1]; /* the first "real" element is the default */
if (s->hw->depth_list[0] == 1) /* only one element in the list -> hide the option */
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
/* halftone */
s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
s->opt[OPT_HALFTONE].desc = SANE_I18N("Selects the halftone");
s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING;
s->opt[OPT_HALFTONE].size = max_string_size(halftone_list_7);
s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
/* XXX use defines */
if (s->hw->level >= 7)
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7;
else if (s->hw->level >= 4)
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4;
else
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list;
s->val[OPT_HALFTONE].w = 1; /* Halftone A */
if (!s->hw->cmd->set_halftoning)
s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* dropout */
s->opt[OPT_DROPOUT].name = "dropout";
s->opt[OPT_DROPOUT].title = SANE_I18N("Dropout");
s->opt[OPT_DROPOUT].desc = SANE_I18N("Selects the dropout");
2006-12-02 14:47:21 +00:00
s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING;
s->opt[OPT_DROPOUT].size = max_string_size(dropout_list);
s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED;
s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_DROPOUT].constraint.string_list = dropout_list;
s->val[OPT_DROPOUT].w = 0; /* None */
2006-12-02 14:47:21 +00:00
/* brightness */
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].desc = SANE_I18N("Selects the brightness");
2006-12-02 14:47:21 +00:00
s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range;
s->val[OPT_BRIGHTNESS].w = 0; /* Normal */
if (!s->hw->cmd->set_bright)
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
/* sharpness */
s->opt[OPT_SHARPNESS].name = "sharpness";
s->opt[OPT_SHARPNESS].title = SANE_I18N("Sharpness");
s->opt[OPT_SHARPNESS].desc = "";
s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT;
s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE;
s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range;
s->val[OPT_SHARPNESS].w = 0; /* Normal */
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->set_outline_emphasis)
s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* gamma */
s->opt[OPT_GAMMA_CORRECTION].name = "gamma-correction";
s->opt[OPT_GAMMA_CORRECTION].title = SANE_I18N("Gamma Correction");
s->opt[OPT_GAMMA_CORRECTION].desc = SANE_I18N("Selects the gamma correction value from a list of pre-defined devices or the user defined table, which can be downloaded to the scanner");
2006-12-02 14:47:21 +00:00
s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING;
s->opt[OPT_GAMMA_CORRECTION].constraint_type =
SANE_CONSTRAINT_STRING_LIST;
2006-12-02 14:47:21 +00:00
/*
* special handling for D1 function level - at this time I'm not
* testing for D1, I'm just assuming that all D level scanners will
* behave the same way. This has to be confirmed with the next D-level
* scanner
*/
if (s->hw->cmd->level[0] == 'D') {
s->opt[OPT_GAMMA_CORRECTION].size =
max_string_size(gamma_list_d);
s->opt[OPT_GAMMA_CORRECTION].constraint.string_list =
gamma_list_d;
s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */
gamma_userdefined = gamma_userdefined_d;
gamma_params = gamma_params_d;
} else {
s->opt[OPT_GAMMA_CORRECTION].size =
max_string_size(gamma_list_ab);
s->opt[OPT_GAMMA_CORRECTION].constraint.string_list =
gamma_list_ab;
s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */
gamma_userdefined = gamma_userdefined_ab;
gamma_params = gamma_params_ab;
}
if (!s->hw->cmd->set_gamma)
s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE;
/* gamma vector */
2006-12-02 14:47:21 +00:00
/*
s->opt[ OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
s->opt[ OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
s->opt[ OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
s->opt[ OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
s->opt[ OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
s->opt[ OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
s->opt[ OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[ OPT_GAMMA_VECTOR].constraint.range = &u8_range;
s->val[ OPT_GAMMA_VECTOR].wa = &s->gamma_table [ 0] [ 0];
*/
/* red gamma vector */
s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof(SANE_Word);
s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0];
/* green gamma vector */
s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof(SANE_Word);
s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0];
2006-12-02 14:47:21 +00:00
/* red gamma vector */
s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
2006-12-02 14:47:21 +00:00
s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof(SANE_Word);
s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0];
2006-12-02 14:47:21 +00:00
if (s->hw->cmd->set_gamma_table
&& gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w] ==
SANE_TRUE) {
2006-12-02 14:47:21 +00:00
/* s->opt[ OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; */
s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
} else {
2006-12-02 14:47:21 +00:00
/* s->opt[ OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; */
s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
}
2006-12-02 14:47:21 +00:00
/* initialize the Gamma tables */
memset(&s->gamma_table[0], 0, 256 * sizeof(SANE_Word));
memset(&s->gamma_table[1], 0, 256 * sizeof(SANE_Word));
memset(&s->gamma_table[2], 0, 256 * sizeof(SANE_Word));
2006-12-02 14:47:21 +00:00
/* memset(&s->gamma_table[3], 0, 256 * sizeof(SANE_Word)); */
for (i = 0; i < 256; i++) {
s->gamma_table[0][i] = i;
s->gamma_table[1][i] = i;
s->gamma_table[2][i] = i;
2006-12-02 14:47:21 +00:00
/* s->gamma_table[3][i] = i; */
}
2006-12-02 14:47:21 +00:00
/* color correction */
s->opt[OPT_COLOR_CORRECTION].name = "color-correction";
s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N("Color correction");
s->opt[OPT_COLOR_CORRECTION].desc =
SANE_I18N
("Sets the color correction table for the selected output device");
2006-12-02 14:47:21 +00:00
s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING;
s->opt[OPT_COLOR_CORRECTION].size = 32;
s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED;
s->opt[OPT_COLOR_CORRECTION].constraint_type =
SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list;
s->val[OPT_COLOR_CORRECTION].w = 5; /* scanner default: CRT monitors */
if (!s->hw->cmd->set_color_correction)
s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* resolution */
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
2006-12-02 14:47:21 +00:00
s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->resolution_list;
s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min;
2006-12-02 14:47:21 +00:00
/* threshold */
s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
2006-12-02 14:47:21 +00:00
s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
s->val[OPT_THRESHOLD].w = 0x80;
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->set_threshold)
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_CCT_GROUP].title =
SANE_I18N("Color correction coefficients");
s->opt[OPT_CCT_GROUP].desc =
SANE_I18N("Matrix multiplication of RGB");
s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED;
2006-12-02 14:47:21 +00:00
/* color correction coefficients */
s->opt[OPT_CCT_1].name = "cct-1";
s->opt[OPT_CCT_2].name = "cct-2";
s->opt[OPT_CCT_3].name = "cct-3";
s->opt[OPT_CCT_4].name = "cct-4";
s->opt[OPT_CCT_5].name = "cct-5";
s->opt[OPT_CCT_6].name = "cct-6";
s->opt[OPT_CCT_7].name = "cct-7";
s->opt[OPT_CCT_8].name = "cct-8";
s->opt[OPT_CCT_9].name = "cct-9";
2006-12-02 14:47:21 +00:00
s->opt[OPT_CCT_1].title = SANE_I18N("Green");
s->opt[OPT_CCT_2].title = SANE_I18N("Shift green to red");
s->opt[OPT_CCT_3].title = SANE_I18N("Shift green to blue");
s->opt[OPT_CCT_4].title = SANE_I18N("Shift red to green");
s->opt[OPT_CCT_5].title = SANE_I18N("Red");
s->opt[OPT_CCT_6].title = SANE_I18N("Shift red to blue");
s->opt[OPT_CCT_7].title = SANE_I18N("Shift blue to green");
s->opt[OPT_CCT_8].title = SANE_I18N("Shift blue to red");
s->opt[OPT_CCT_9].title = SANE_I18N("Blue");
s->opt[OPT_CCT_1].desc = SANE_I18N("Controls green level");
s->opt[OPT_CCT_2].desc =
SANE_I18N("Adds to red based on green level");
s->opt[OPT_CCT_3].desc =
SANE_I18N("Adds to blue based on green level");
s->opt[OPT_CCT_4].desc =
SANE_I18N("Adds to green based on red level");
s->opt[OPT_CCT_5].desc = SANE_I18N("Controls red level");
s->opt[OPT_CCT_6].desc = SANE_I18N("Adds to blue based on red level");
s->opt[OPT_CCT_7].desc =
SANE_I18N("Adds to green based on blue level");
s->opt[OPT_CCT_8].desc = SANE_I18N("Adds to red based on blue level");
s->opt[OPT_CCT_9].desc = SANE_I18N("Controls blue level");
s->opt[OPT_CCT_1].type = SANE_TYPE_INT;
s->opt[OPT_CCT_2].type = SANE_TYPE_INT;
s->opt[OPT_CCT_3].type = SANE_TYPE_INT;
s->opt[OPT_CCT_4].type = SANE_TYPE_INT;
s->opt[OPT_CCT_5].type = SANE_TYPE_INT;
s->opt[OPT_CCT_6].type = SANE_TYPE_INT;
s->opt[OPT_CCT_7].type = SANE_TYPE_INT;
s->opt[OPT_CCT_8].type = SANE_TYPE_INT;
s->opt[OPT_CCT_9].type = SANE_TYPE_INT;
2006-12-02 14:47:21 +00:00
s->opt[OPT_CCT_1].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_2].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_3].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_4].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_5].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_6].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_7].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_8].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
s->opt[OPT_CCT_9].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
s->opt[OPT_CCT_1].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_2].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_3].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_4].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_5].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_6].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_7].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_8].unit = SANE_UNIT_NONE;
s->opt[OPT_CCT_9].unit = SANE_UNIT_NONE;
2006-12-02 14:47:21 +00:00
s->opt[OPT_CCT_1].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_2].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_3].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_4].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_5].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_6].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_7].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_8].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_9].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CCT_1].constraint.range = &s8_range;
s->opt[OPT_CCT_2].constraint.range = &s8_range;
s->opt[OPT_CCT_3].constraint.range = &s8_range;
s->opt[OPT_CCT_4].constraint.range = &s8_range;
s->opt[OPT_CCT_5].constraint.range = &s8_range;
s->opt[OPT_CCT_6].constraint.range = &s8_range;
s->opt[OPT_CCT_7].constraint.range = &s8_range;
s->opt[OPT_CCT_8].constraint.range = &s8_range;
s->opt[OPT_CCT_9].constraint.range = &s8_range;
2006-12-02 14:47:21 +00:00
s->val[OPT_CCT_1].w = 32;
s->val[OPT_CCT_2].w = 0;
s->val[OPT_CCT_3].w = 0;
s->val[OPT_CCT_4].w = 0;
s->val[OPT_CCT_5].w = 32;
s->val[OPT_CCT_6].w = 0;
s->val[OPT_CCT_7].w = 0;
s->val[OPT_CCT_8].w = 0;
s->val[OPT_CCT_9].w = 32;
2006-12-02 14:47:21 +00:00
/* "Advanced" group: */
s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N("Advanced");
s->opt[OPT_ADVANCED_GROUP].desc = "";
s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
2006-12-02 14:47:21 +00:00
/* mirror */
s->opt[OPT_MIRROR].name = "mirror";
s->opt[OPT_MIRROR].title = SANE_I18N("Mirror image");
s->opt[OPT_MIRROR].desc = SANE_I18N("Mirror the image");
2006-12-02 14:47:21 +00:00
s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
s->val[OPT_MIRROR].w = SANE_FALSE;
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->mirror_image)
s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* auto area segmentation */
s->opt[OPT_AAS].name = "auto-area-segmentation";
s->opt[OPT_AAS].title = SANE_I18N("Auto area segmentation");
s->opt[OPT_AAS].desc =
"Enables different dithering modes in image and text areas";
2006-12-02 14:47:21 +00:00
s->opt[OPT_AAS].type = SANE_TYPE_BOOL;
s->val[OPT_AAS].w = SANE_TRUE;
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->control_auto_area_segmentation)
s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* limit resolution list */
s->opt[OPT_LIMIT_RESOLUTION].name = "short-resolution";
s->opt[OPT_LIMIT_RESOLUTION].title =
SANE_I18N("Short resolution list");
s->opt[OPT_LIMIT_RESOLUTION].desc =
SANE_I18N("Display a shortened resolution list");
s->opt[OPT_LIMIT_RESOLUTION].type = SANE_TYPE_BOOL;
s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE;
2006-12-02 14:47:21 +00:00
/* "Preview settings" group: */
s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW;
s->opt[OPT_PREVIEW_GROUP].desc = "";
s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED;
2006-12-02 14:47:21 +00:00
/* preview */
s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
s->val[OPT_PREVIEW].w = SANE_FALSE;
/* "Geometry" group: */
s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
s->opt[OPT_GEOMETRY_GROUP].desc = "";
s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
2006-12-02 14:47:21 +00:00
/* top-left x */
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
2006-12-02 14:47:21 +00:00
s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TL_X].constraint.range = s->hw->x_range;
s->val[OPT_TL_X].w = 0;
2006-12-02 14:47:21 +00:00
/* top-left y */
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
2006-12-02 14:47:21 +00:00
s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TL_Y].constraint.range = s->hw->y_range;
s->val[OPT_TL_Y].w = 0;
2006-12-02 14:47:21 +00:00
/* bottom-right x */
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
s->val[OPT_BR_X].w = s->hw->x_range->max;
2006-12-02 14:47:21 +00:00
/* bottom-right y */
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
2006-12-02 14:47:21 +00:00
s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
s->val[OPT_BR_Y].w = s->hw->y_range->max;
2006-12-02 14:47:21 +00:00
/* "Optional equipment" group: */
s->opt[OPT_EQU_GROUP].title = SANE_I18N("Optional equipment");
s->opt[OPT_EQU_GROUP].desc = "";
s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED;
2006-12-02 14:47:21 +00:00
/* source */
s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
2006-12-02 14:47:21 +00:00
s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
s->opt[OPT_SOURCE].size = max_string_size(source_list);
2006-12-02 14:47:21 +00:00
s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_SOURCE].constraint.string_list = source_list;
2006-12-02 14:47:21 +00:00
if (!s->hw->extension)
s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */
2006-12-02 14:47:21 +00:00
/* film type */
s->opt[OPT_FILM_TYPE].name = "film-type";
s->opt[OPT_FILM_TYPE].title = SANE_I18N("Film type");
s->opt[OPT_FILM_TYPE].desc = "";
s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING;
s->opt[OPT_FILM_TYPE].size = max_string_size(film_list);
s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_FILM_TYPE].constraint.string_list = film_list;
s->val[OPT_FILM_TYPE].w = 0;
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->set_bay)
s->opt[OPT_FILM_TYPE].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* focus position */
s->opt[OPT_FOCUS].name = "focus-position";
s->opt[OPT_FOCUS].title = SANE_I18N("Focus Position");
s->opt[OPT_FOCUS].desc = SANE_I18N("Sets the focus position to either the glass or 2.5mm above the glass");
s->opt[OPT_FOCUS].type = SANE_TYPE_STRING;
s->opt[OPT_FOCUS].size = max_string_size(focus_list);
s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_FOCUS].constraint.string_list = focus_list;
s->val[OPT_FOCUS].w = 0;
s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED;
if (s->hw->focusSupport == SANE_TRUE)
s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE;
else
s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* forward feed / eject */
s->opt[OPT_EJECT].name = "eject";
s->opt[OPT_EJECT].title = SANE_I18N("Eject");
s->opt[OPT_EJECT].desc = SANE_I18N("Eject the sheet in the ADF");
s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON;
2006-12-02 14:47:21 +00:00
if ((!s->hw->ADF) && (!s->hw->cmd->set_bay)) { /* Hack: Using set_bay to indicate. */
s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
}
/* auto forward feed / eject */
s->opt[OPT_AUTO_EJECT].name = "auto-eject";
s->opt[OPT_AUTO_EJECT].title = SANE_I18N("Auto eject");
s->opt[OPT_AUTO_EJECT].desc =
SANE_I18N("Eject document after scanning");
s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL;
s->val[OPT_AUTO_EJECT].w = SANE_FALSE;
2006-12-02 14:47:21 +00:00
if (!s->hw->ADF)
s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
s->opt[OPT_ADF_MODE].name = "adf-mode";
s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode");
s->opt[OPT_ADF_MODE].desc =
SANE_I18N("Selects the ADF mode (simplex/duplex)");
s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING;
s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list);
s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list;
s->val[OPT_ADF_MODE].w = 0; /* simplex */
2006-12-02 14:47:21 +00:00
if ((!s->hw->ADF) || (s->hw->duplex == SANE_FALSE))
s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
/* select bay */
s->opt[OPT_BAY].name = "bay";
s->opt[OPT_BAY].title = SANE_I18N("Bay");
s->opt[OPT_BAY].desc = SANE_I18N("Select bay to scan");
s->opt[OPT_BAY].type = SANE_TYPE_STRING;
s->opt[OPT_BAY].size = max_string_size(bay_list);
s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_BAY].constraint.string_list = bay_list;
s->val[OPT_BAY].w = 0; /* Bay 1 */
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->set_bay)
s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
s->opt[OPT_WAIT_FOR_BUTTON].name = "wait-for-button";
s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_I18N("Wait for button");
s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_I18N("After sending the scan command, wait until the button on the scanner is pressed");
2006-12-02 14:47:21 +00:00
s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL;
s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE;
s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE;
s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL;
s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED;
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->request_push_button_status)
s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE;
2006-12-02 14:47:21 +00:00
return SANE_STATUS_GOOD;
}
2006-12-02 14:47:21 +00:00
SANE_Status
sane_open(SANE_String_Const name, SANE_Handle *handle)
{
SANE_Status status;
Epson_Device *dev;
Epson_Scanner *s;
2006-12-02 14:47:21 +00:00
DBG(7, "%s: name = %s\n", __func__, name);
2006-12-02 14:47:21 +00:00
/* do a scan if the devices list is empty */
if (first_dev == NULL)
probe_devices();
/* search for device */
if (name[0]) {
for (dev = first_dev; dev; dev = dev->next)
if (strcmp(dev->sane.name, name) == 0)
break;
} else
dev = first_dev;
if (!dev) {
DBG(1, "error opening the device\n");
return SANE_STATUS_INVAL;
2006-12-02 14:47:21 +00:00
}
s = malloc(sizeof(Epson_Scanner));
if (!s) {
DBG(1, "out of memory (line %d)\n", __LINE__);
return SANE_STATUS_NO_MEM;
2006-12-02 14:47:21 +00:00
}
memset(s, 0x00, sizeof(struct Epson_Scanner));
s->fd = -1;
s->hw = dev;
init_options(s);
*handle = (SANE_Handle) s;
2006-12-02 14:47:21 +00:00
status = open_scanner(s);
if (status != SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
status = esci_reset(s);
if (status != SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
return status;
}
2006-12-02 14:47:21 +00:00
void
sane_close(SANE_Handle handle)
{
int i;
Epson_Scanner *s;
2006-12-02 14:47:21 +00:00
/*
* XXX Test if there is still data pending from
* the scanner. If so, then do a cancel
*/
2006-12-02 14:47:21 +00:00
s = (Epson_Scanner *) handle;
if (s->fd != -1)
close_scanner(s);
for (i = 0; i < LINES_SHUFFLE_MAX; i++) {
if (s->line_buffer[i] != NULL)
free(s->line_buffer[i]);
}
2006-12-02 14:47:21 +00:00
free(s);
}
2006-12-02 14:47:21 +00:00
const SANE_Option_Descriptor *
sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
{
Epson_Scanner *s = (Epson_Scanner *) handle;
if (option < 0 || option >= NUM_OPTIONS)
return NULL;
2006-12-02 14:47:21 +00:00
return s->opt + option;
}
2006-12-02 14:47:21 +00:00
static const SANE_String_Const *
search_string_list(const SANE_String_Const *list, SANE_String value)
{
while (*list != NULL && strcmp(value, *list) != 0)
list++;
return ((*list == NULL) ? NULL : list);
2006-12-02 14:47:21 +00:00
}
/*
Activate, deactivate an option. Subroutines so we can add
debugging info if we want. The change flag is set to TRUE
if we changed an option. If we did not change an option,
then the value of the changed flag is not modified.
*/
2006-12-02 14:47:21 +00:00
static void
activateOption(Epson_Scanner *s, SANE_Int option, SANE_Bool *change)
2006-12-02 14:47:21 +00:00
{
if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
s->opt[option].cap &= ~SANE_CAP_INACTIVE;
*change = SANE_TRUE;
}
}
2006-12-02 14:47:21 +00:00
static void
deactivateOption(Epson_Scanner *s, SANE_Int option, SANE_Bool *change)
{
if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) {
s->opt[option].cap |= SANE_CAP_INACTIVE;
*change = SANE_TRUE;
}
}
2006-12-02 14:47:21 +00:00
static void
setOptionState(Epson_Scanner *s, SANE_Bool state, SANE_Int option,
SANE_Bool *change)
{
if (state)
activateOption(s, option, change);
else
deactivateOption(s, option, change);
}
2006-12-02 14:47:21 +00:00
static SANE_Status
getvalue(SANE_Handle handle, SANE_Int option, void *value)
{
Epson_Scanner *s = (Epson_Scanner *) handle;
SANE_Option_Descriptor *sopt = &(s->opt[option]);
Option_Value *sval = &(s->val[option]);
2006-12-02 14:47:21 +00:00
DBG(17, "%s: option = %d\n", __func__, option);
switch (option) {
2006-12-02 14:47:21 +00:00
/* case OPT_GAMMA_VECTOR: */
case OPT_GAMMA_VECTOR_R:
case OPT_GAMMA_VECTOR_G:
case OPT_GAMMA_VECTOR_B:
memcpy(value, sval->wa, sopt->size);
break;
2006-12-02 14:47:21 +00:00
case OPT_NUM_OPTS:
case OPT_RESOLUTION:
case OPT_TL_X:
case OPT_TL_Y:
case OPT_BR_X:
case OPT_BR_Y:
case OPT_MIRROR:
case OPT_AAS:
case OPT_PREVIEW:
case OPT_BRIGHTNESS:
case OPT_SHARPNESS:
case OPT_AUTO_EJECT:
case OPT_CCT_1:
case OPT_CCT_2:
case OPT_CCT_3:
case OPT_CCT_4:
case OPT_CCT_5:
case OPT_CCT_6:
case OPT_CCT_7:
case OPT_CCT_8:
case OPT_CCT_9:
case OPT_THRESHOLD:
case OPT_BIT_DEPTH:
case OPT_WAIT_FOR_BUTTON:
case OPT_LIMIT_RESOLUTION:
*((SANE_Word *) value) = sval->w;
break;
2006-12-02 14:47:21 +00:00
case OPT_MODE:
case OPT_ADF_MODE:
case OPT_HALFTONE:
case OPT_DROPOUT:
case OPT_SOURCE:
case OPT_FILM_TYPE:
case OPT_GAMMA_CORRECTION:
case OPT_COLOR_CORRECTION:
case OPT_BAY:
case OPT_FOCUS:
strcpy((char *) value, sopt->constraint.string_list[sval->w]);
break;
2006-12-02 14:47:21 +00:00
default:
return SANE_STATUS_INVAL;
2006-12-02 14:47:21 +00:00
}
return SANE_STATUS_GOOD;
}
2006-12-02 14:47:21 +00:00
/*
* This routine handles common options between OPT_MODE and
* OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS
* - auto area segmentation, and threshold. Apparently AAS
* is some method to differentiate between text and photos.
* Or something like that.
*
* AAS is available when the scan color depth is 1 and the
* halftone method is not TET.
*
* Threshold is available when halftone is NONE, and depth is 1.
*/
static void
handle_depth_halftone(Epson_Scanner *s, SANE_Bool *reload)
{
int hti = s->val[OPT_HALFTONE].w;
int mdi = s->val[OPT_MODE].w;
SANE_Bool aas = SANE_FALSE;
SANE_Bool thresh = SANE_FALSE;
2006-12-02 14:47:21 +00:00
if (!s->hw->cmd->control_auto_area_segmentation)
return;
2006-12-02 14:47:21 +00:00
if (mode_params[mdi].depth == 1) {
if (halftone_params[hti] != HALFTONE_TET)
aas = SANE_TRUE;
2006-12-02 14:47:21 +00:00
if (halftone_params[hti] == HALFTONE_NONE)
thresh = SANE_TRUE;
2006-12-02 14:47:21 +00:00
}
setOptionState(s, aas, OPT_AAS, reload);
setOptionState(s, thresh, OPT_THRESHOLD, reload);
}
/*
* Handles setting the source (flatbed, transparency adapter (TPU),
* or auto document feeder (ADF)).
*
* For newer scanners it also sets the focus according to the
* glass / TPU settings.
*/
2006-12-02 14:47:21 +00:00
static void
change_source(Epson_Scanner *s, SANE_Int optindex, char *value)
{
int force_max = SANE_FALSE;
SANE_Bool dummy;
2006-12-02 14:47:21 +00:00
DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex,
value);
2006-12-02 14:47:21 +00:00
/* reset the scanner when we are changing the source setting -
this is necessary for the Perfection 1650 */
if (s->hw->need_reset_on_source_change)
esci_reset(s);
2006-12-02 14:47:21 +00:00
s->focusOnGlass = SANE_TRUE; /* this is the default */
2006-12-02 14:47:21 +00:00
if (s->val[OPT_SOURCE].w == optindex)
return;
2006-12-02 14:47:21 +00:00
s->val[OPT_SOURCE].w = optindex;
2006-12-02 14:47:21 +00:00
if (s->val[OPT_TL_X].w == s->hw->x_range->min
&& s->val[OPT_TL_Y].w == s->hw->y_range->min
&& s->val[OPT_BR_X].w == s->hw->x_range->max
&& s->val[OPT_BR_Y].w == s->hw->y_range->max) {
force_max = SANE_TRUE;
2006-12-02 14:47:21 +00:00
}
if (strcmp(ADF_STR, value) == 0) {
s->hw->x_range = &s->hw->adf_x_range;
s->hw->y_range = &s->hw->adf_y_range;
s->hw->use_extension = SANE_TRUE;
/* disable film type option */
deactivateOption(s, OPT_FILM_TYPE, &dummy);
s->val[OPT_FOCUS].w = 0;
if (s->hw->duplex) {
activateOption(s, OPT_ADF_MODE, &dummy);
} else {
deactivateOption(s, OPT_ADF_MODE, &dummy);
s->val[OPT_ADF_MODE].w = 0;
}
2006-12-02 14:47:21 +00:00
DBG(1, "adf activated (%d %d)\n", s->hw->use_extension,
s->hw->duplex);
2006-12-02 14:47:21 +00:00
} else if (strcmp(TPU_STR, value) == 0) {
s->hw->x_range = &s->hw->tpu_x_range;
s->hw->y_range = &s->hw->tpu_y_range;
s->hw->use_extension = SANE_TRUE;
2006-12-02 14:47:21 +00:00
/* enable film type option only if the scanner supports it */
if (s->hw->cmd->set_film_type != 0)
activateOption(s, OPT_FILM_TYPE, &dummy);
else
deactivateOption(s, OPT_FILM_TYPE, &dummy);
2006-12-02 14:47:21 +00:00
/* enable focus position if the scanner supports it */
if (s->hw->cmd->set_focus_position != 0) {
s->val[OPT_FOCUS].w = 1;
s->focusOnGlass = SANE_FALSE;
}
2006-12-02 14:47:21 +00:00
deactivateOption(s, OPT_ADF_MODE, &dummy);
deactivateOption(s, OPT_EJECT, &dummy);
deactivateOption(s, OPT_AUTO_EJECT, &dummy);
} else {
/* neither ADF nor TPU active */
s->hw->x_range = &s->hw->fbf_x_range;
s->hw->y_range = &s->hw->fbf_y_range;
s->hw->use_extension = SANE_FALSE;
2006-12-02 14:47:21 +00:00
/* disable film type option */
deactivateOption(s, OPT_FILM_TYPE, &dummy);
s->val[OPT_FOCUS].w = 0;
deactivateOption(s, OPT_ADF_MODE, &dummy);
}
2006-12-02 14:47:21 +00:00
/* special handling for FilmScan 200 */
if (s->hw->cmd->level[0] == 'F')
activateOption(s, OPT_FILM_TYPE, &dummy);
s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max)
s->val[OPT_TL_X].w = s->hw->x_range->min;
if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max)
s->val[OPT_TL_Y].w = s->hw->y_range->min;
if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max)
s->val[OPT_BR_X].w = s->hw->x_range->max;
if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max)
s->val[OPT_BR_Y].w = s->hw->y_range->max;
setOptionState(s, s->hw->ADF
&& s->hw->use_extension, OPT_AUTO_EJECT, &dummy);
setOptionState(s, s->hw->ADF
&& s->hw->use_extension, OPT_EJECT, &dummy);
}
2006-12-02 14:47:21 +00:00
static SANE_Status
setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
2006-12-02 14:47:21 +00:00
{
Epson_Scanner *s = (Epson_Scanner *) handle;
SANE_Option_Descriptor *sopt = &(s->opt[option]);
Option_Value *sval = &(s->val[option]);
2006-12-02 14:47:21 +00:00
SANE_Status status;
const SANE_String_Const *optval;
int optindex;
SANE_Bool reload = SANE_FALSE;
2006-12-02 14:47:21 +00:00
DBG(17, "%s: option = %d, value = %p\n", __func__, option, value);
status = sanei_constrain_value(sopt, value, info);
if (status != SANE_STATUS_GOOD)
return status;
if (info && value && (*info & SANE_INFO_INEXACT)
&& sopt->type == SANE_TYPE_INT)
DBG(17, "%s: constrained val = %d\n", __func__,
*(SANE_Word *) value);
2006-12-02 14:47:21 +00:00
s->option_has_changed = SANE_TRUE;
optval = NULL;
optindex = 0;
if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) {
optval = search_string_list(sopt->constraint.string_list,
(char *) value);
2006-12-02 14:47:21 +00:00
if (optval == NULL)
return SANE_STATUS_INVAL;
optindex = optval - sopt->constraint.string_list;
2006-12-02 14:47:21 +00:00
}
switch (option) {
2006-12-02 14:47:21 +00:00
/* case OPT_GAMMA_VECTOR: */
case OPT_GAMMA_VECTOR_R:
case OPT_GAMMA_VECTOR_G:
case OPT_GAMMA_VECTOR_B:
memcpy(sval->wa, value, sopt->size); /* Word arrays */
break;
2006-12-02 14:47:21 +00:00
case OPT_CCT_1:
case OPT_CCT_2:
case OPT_CCT_3:
case OPT_CCT_4:
case OPT_CCT_5:
case OPT_CCT_6:
case OPT_CCT_7:
case OPT_CCT_8:
case OPT_CCT_9:
sval->w = *((SANE_Word *) value); /* Simple values */
break;
2006-12-02 14:47:21 +00:00
case OPT_DROPOUT:
case OPT_FILM_TYPE:
case OPT_BAY:
case OPT_FOCUS:
sval->w = optindex; /* Simple lists */
break;
2006-12-02 14:47:21 +00:00
case OPT_EJECT:
/* XXX required? control_extension(s, 1); */
esci_eject(s);
break;
case OPT_RESOLUTION:
sval->w = *((SANE_Word *) value);
DBG(17, "setting resolution to %d\n", sval->w);
reload = SANE_TRUE;
break;
case OPT_BR_X:
case OPT_BR_Y:
sval->w = *((SANE_Word *) value);
if (SANE_UNFIX(sval->w) == 0) {
DBG(17, "invalid br-x or br-y\n");
return SANE_STATUS_INVAL;
}
/* passthru */
case OPT_TL_X:
case OPT_TL_Y:
sval->w = *((SANE_Word *) value);
DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w));
if (NULL != info)
*info |= SANE_INFO_RELOAD_PARAMS;
break;
2006-12-02 14:47:21 +00:00
case OPT_SOURCE:
change_source(s, optindex, (char *) value);
reload = SANE_TRUE;
break;
2006-12-02 14:47:21 +00:00
case OPT_MODE:
{
SANE_Bool isColor = mode_params[optindex].color;
SANE_Bool userDefined =
color_userdefined[s->val[OPT_COLOR_CORRECTION].w];
2006-12-02 14:47:21 +00:00
sval->w = optindex;
2006-12-02 14:47:21 +00:00
if (s->hw->cmd->set_halftoning != 0)
setOptionState(s,
mode_params[optindex].depth ==
1, OPT_HALFTONE, &reload);
2006-12-02 14:47:21 +00:00
setOptionState(s, !isColor, OPT_DROPOUT, &reload);
2006-12-02 14:47:21 +00:00
if (s->hw->cmd->set_color_correction)
setOptionState(s, isColor,
OPT_COLOR_CORRECTION, &reload);
2006-12-02 14:47:21 +00:00
if (s->hw->cmd->set_color_correction_coefficients) {
setOptionState(s, isColor
&& userDefined, OPT_CCT_1, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_2, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_3, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_4, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_5, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_6, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_7, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_8, &reload);
setOptionState(s, isColor
&& userDefined, OPT_CCT_9, &reload);
}
2006-12-02 14:47:21 +00:00
/* if binary, then disable the bit depth selection */
if (optindex == 0) {
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
} else {
if (s->hw->depth_list[0] == 1)
s->opt[OPT_BIT_DEPTH].cap |=
SANE_CAP_INACTIVE;
else {
s->opt[OPT_BIT_DEPTH].cap &=
~SANE_CAP_INACTIVE;
s->val[OPT_BIT_DEPTH].w =
mode_params[optindex].depth;
}
2006-12-02 14:47:21 +00:00
}
handle_depth_halftone(s, &reload);
reload = SANE_TRUE;
2006-12-02 14:47:21 +00:00
break;
2006-12-02 14:47:21 +00:00
}
case OPT_ADF_MODE:
sval->w = optindex;
break;
2006-12-02 14:47:21 +00:00
case OPT_BIT_DEPTH:
sval->w = *((SANE_Word *) value);
mode_params[s->val[OPT_MODE].w].depth = sval->w;
reload = SANE_TRUE;
break;
2006-12-02 14:47:21 +00:00
case OPT_HALFTONE:
sval->w = optindex;
handle_depth_halftone(s, &reload);
break;
case OPT_COLOR_CORRECTION:
{
SANE_Bool f = color_userdefined[optindex];
2006-12-02 14:47:21 +00:00
sval->w = optindex;
setOptionState(s, f, OPT_CCT_1, &reload);
setOptionState(s, f, OPT_CCT_2, &reload);
setOptionState(s, f, OPT_CCT_3, &reload);
setOptionState(s, f, OPT_CCT_4, &reload);
setOptionState(s, f, OPT_CCT_5, &reload);
setOptionState(s, f, OPT_CCT_6, &reload);
setOptionState(s, f, OPT_CCT_7, &reload);
setOptionState(s, f, OPT_CCT_8, &reload);
setOptionState(s, f, OPT_CCT_9, &reload);
2006-12-02 14:47:21 +00:00
break;
2006-12-02 14:47:21 +00:00
}
case OPT_GAMMA_CORRECTION:
{
SANE_Bool f = gamma_userdefined[optindex];
sval->w = optindex;
/* setOptionState(s, f, OPT_GAMMA_VECTOR, &reload ); */
setOptionState(s, f, OPT_GAMMA_VECTOR_R, &reload);
setOptionState(s, f, OPT_GAMMA_VECTOR_G, &reload);
setOptionState(s, f, OPT_GAMMA_VECTOR_B, &reload);
setOptionState(s, !f, OPT_BRIGHTNESS, &reload); /* Note... */
break;
}
case OPT_MIRROR:
case OPT_AAS:
case OPT_PREVIEW: /* needed? */
case OPT_BRIGHTNESS:
case OPT_SHARPNESS:
case OPT_AUTO_EJECT:
case OPT_THRESHOLD:
case OPT_WAIT_FOR_BUTTON:
sval->w = *((SANE_Word *) value);
break;
case OPT_LIMIT_RESOLUTION:
sval->w = *((SANE_Word *) value);
filter_resolution_list(s);
reload = SANE_TRUE;
break;
default:
return SANE_STATUS_INVAL;
}
if (reload && info != NULL)
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
DBG(17, "%s: end\n", __func__);
return SANE_STATUS_GOOD;
}
SANE_Status
sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action,
void *value, SANE_Int *info)
{
DBG(17, "%s: action = %x, option = %d\n", __func__, action, option);
if (option < 0 || option >= NUM_OPTIONS)
return SANE_STATUS_INVAL;
if (info != NULL)
*info = 0;
switch (action) {
case SANE_ACTION_GET_VALUE:
return getvalue(handle, option, value);
case SANE_ACTION_SET_VALUE:
return setvalue(handle, option, value, info);
default:
return SANE_STATUS_INVAL;
}
return SANE_STATUS_INVAL;
}
SANE_Status
sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
{
Epson_Scanner *s = (Epson_Scanner *) handle;
int dpi, max_x, max_y;
int bytes_per_pixel;
DBG(5, "%s\n", __func__);
/*
* If sane_start was already called, then just retrieve the parameters
* from the scanner data structure
*/
if (!s->eof && s->ptr != NULL) {
DBG(5, "returning saved params structure\n");
if (params != NULL) {
DBG(1,
"restoring parameters from saved parameters\n");
*params = s->params;
}
DBG(5, "resolution = %d, preview = %d\n",
s->val[OPT_RESOLUTION].w, s->val[OPT_PREVIEW].w);
DBG(5, "get para tlx %f tly %f brx %f bry %f [mm]\n",
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));
print_params(s->params);
return SANE_STATUS_GOOD;
}
/* otherwise initialize the params structure and gather the data */
memset(&s->params, 0, sizeof(SANE_Parameters));
dpi = s->val[OPT_RESOLUTION].w;
max_x = max_y = 0;
/* XXX check this */
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;
/*
* Make sure that the number of lines is correct for color shuffling:
* The shuffling alghorithm produces 2xline_distance lines at the
* beginning and the same amount at the end of the scan that are not
* useable. If s->params.lines gets negative, 0 lines are reported
* back to the frontend.
*/
if (s->hw->color_shuffle) {
s->params.lines -= 4 * s->line_distance;
if (s->params.lines < 0)
s->params.lines = 0;
DBG(1, "adjusted params.lines for color_shuffle by %d to %d\n",
4 * s->line_distance, s->params.lines);
}
DBG(5, "resolution = %d, preview = %d\n",
s->val[OPT_RESOLUTION].w, s->val[OPT_PREVIEW].w);
DBG(5, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n",
(void *) s, (void *) s->val, 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));
/*
* 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_BIT_DEPTH].w;
if (s->params.depth > 8) {
s->params.depth = 16; /*
* The frontends can only handle 8 or 16 bits
* for gray or color - so if it's more than 8,
* it gets automatically set to 16. This works
* as long as EPSON does not come out with a
* scanner that can handle more than 16 bits
* per color channel.
*/
}
bytes_per_pixel = s->params.depth / 8; /* this works because it can only be set to 1, 8 or 16 */
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;
if (mode_params[s->val[OPT_MODE].w].color) {
s->params.format = SANE_FRAME_RGB;
s->params.bytes_per_line =
3 * s->params.pixels_per_line * bytes_per_pixel;
} else {
s->params.format = SANE_FRAME_GRAY;
s->params.bytes_per_line =
s->params.pixels_per_line * s->params.depth / 8;
}
if (NULL != params)
*params = s->params;
print_params(s->params);
return SANE_STATUS_GOOD;
}
2006-12-02 14:47:21 +00:00
/*
* This function is part of the SANE API and gets called from the front end to
* start the scan process.
*/
SANE_Status
sane_start(SANE_Handle handle)
{
Epson_Scanner *s = (Epson_Scanner *) handle;
Epson_Device *dev = s->hw;
2006-12-02 14:47:21 +00:00
SANE_Status status;
DBG(5, "%s\n", __func__);
2006-12-02 14:47:21 +00:00
/* check if we just have finished working with the ADF */
status = e2_check_adf(s);
if (status != SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
/* calc scanning parameters */
status = e2_init_parameters(s);
if (status != SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
/* ESC , bay */
if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BAY].cap)) {
status = esci_set_bay(s, s->val[OPT_BAY].w);
2006-12-02 14:47:21 +00:00
if (status != SANE_STATUS_GOOD)
return status;
}
/* set scanning parameters */
if (dev->extended_commands)
status = e2_set_extended_scanning_parameters(s);
else
status = e2_set_scanning_parameters(s);
2006-12-02 14:47:21 +00:00
if (status != SANE_STATUS_GOOD)
return status;
/* ESC z, user defined gamma table */
if (dev->cmd->set_gamma_table
2006-12-02 14:47:21 +00:00
&& gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w]) {
status = esci_set_gamma_table(s);
2006-12-02 14:47:21 +00:00
if (status != SANE_STATUS_GOOD)
return status;
}
/* ESC m, user defined color correction */
if (s->val[OPT_COLOR_CORRECTION].w == 1) {
status = esci_set_color_correction_coefficients(s);
2006-12-02 14:47:21 +00:00
if (status != SANE_STATUS_GOOD)
return status;
}
/* check if we just have finished working with the ADF.
* this seems to work only after the scanner has been
* set up with scanning parameters
*/
status = e2_check_adf(s);
if (status != SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
/*
status = sane_get_parameters(handle, NULL);
if (status != SANE_STATUS_GOOD)
return status;
*/
/*
* If WAIT_FOR_BUTTON is active, then do just that:
* Wait until the button is pressed. If the button was already
* pressed, then we will get the button pressed event right away.
*/
if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE)
e2_wait_button(s);
2006-12-02 14:47:21 +00:00
/* for debug, request command parameter */
/* if (DBG_LEVEL) {
2006-12-02 14:47:21 +00:00
unsigned char buf[45];
request_command_parameter(s, buf);
}
*/
2006-12-02 14:47:21 +00:00
/* set the retry count to 0 */
s->retry_count = 0;
/* allocate buffers for color shuffling */
if (dev->color_shuffle == SANE_TRUE) {
int i;
2006-12-02 14:47:21 +00:00
/* initialize the line buffers */
for (i = 0; i < s->line_distance * 2 + 1; i++) {
2006-12-02 14:47:21 +00:00
if (s->line_buffer[i] != NULL)
free(s->line_buffer[i]);
s->line_buffer[i] = malloc(s->params.bytes_per_line);
if (s->line_buffer[i] == NULL) {
DBG(1, "out of memory (line %d)\n", __LINE__);
return SANE_STATUS_NO_MEM;
}
}
}
/* prepare buffer here so that a memory allocation failure
* will leave the scanner in a sane state.
* the buffer will have to hold the image data plus
* an error code in the extended handshaking mode.
*/
s->buf = realloc(s->buf, (s->lcount * s->params.bytes_per_line) + 1);
if (s->buf == NULL)
return SANE_STATUS_NO_MEM;
2006-12-02 14:47:21 +00:00
s->eof = SANE_FALSE;
s->ptr = s->end = s->buf;
s->canceling = SANE_FALSE;
/* feed the first sheet in the ADF */
if (dev->ADF && dev->use_extension && dev->cmd->feed) {
status = esci_feed(s);
if (status != SANE_STATUS_GOOD)
return status;
}
2006-12-02 14:47:21 +00:00
/* this seems to work only for some devices */
status = e2_wait_warm_up(s);
if (status != SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
/* start scanning */
DBG(1, "%s: scanning...\n", __func__);
2006-12-02 14:47:21 +00:00
if (dev->extended_commands) {
status = e2_start_ext_scan(s);
/* sometimes the scanner gives an io error when
* it's warming up.
*/
if (status == SANE_STATUS_IO_ERROR) {
status = e2_wait_warm_up(s);
if (status == SANE_STATUS_GOOD)
status = e2_start_ext_scan(s);
}
} else
status = e2_start_std_scan(s);
if (status != SANE_STATUS_GOOD) {
DBG(1, "%s: start failed: %s\n", __func__,
sane_strstatus(status));
2009-04-25 13:55:06 +00:00
return status;
}
2009-04-25 13:55:06 +00:00
/* this is a kind of read request */
if (dev->connection == SANE_EPSON_NET) {
sanei_epson_net_write(s, 0x2000, NULL, 0,
s->ext_block_len + 1, &status);
}
return status;
2006-12-02 14:47:21 +00:00
}
static inline int
get_color(int status)
{
switch ((status >> 2) & 0x03) {
case 1:
return 1;
case 2:
return 0;
case 3:
return 2;
default:
return 0; /* required to make the compiler happy */
}
}
/* this moves data from our buffers to SANE */
SANE_Status
sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length,
SANE_Int *length)
{
SANE_Status status;
Epson_Scanner *s = (Epson_Scanner *) handle;
2006-12-02 14:47:21 +00:00
*length = 0;
if (s->hw->extended_commands)
status = e2_ext_sane_read(handle);
else
status = e2_block_sane_read(handle);
DBG(18, "moving data %d %p %p\n", max_length, s->ptr, s->end);
e2_copy_image_data(s, data, max_length, length);
2006-12-02 14:47:21 +00:00
/* continue reading if appropriate */
if (status == SANE_STATUS_GOOD)
return status;
2006-12-02 14:47:21 +00:00
e2_scan_finish(s);
2006-12-02 14:47:21 +00:00
return status;
2006-12-02 14:47:21 +00:00
}
/*
* void sane_cancel(SANE_Handle handle)
*
* Set the cancel flag to true. The next time the backend requests data
* from the scanner the CAN message will be sent.
*/
void
sane_cancel(SANE_Handle handle)
{
Epson_Scanner *s = (Epson_Scanner *) handle;
SANE_Status status = SANE_STATUS_GOOD;
2006-12-02 14:47:21 +00:00
/*
* If the s->ptr pointer is not NULL, then a scan operation
* was started and if s->eof is FALSE, it was not finished.
*/
if (s->buf) {
2006-12-02 14:47:21 +00:00
unsigned char *dummy;
int len;
/* malloc one line */
dummy = malloc(s->params.bytes_per_line);
if (dummy == NULL) {
DBG(1, "Out of memory\n");
return;
}
2006-12-02 14:47:21 +00:00
/* there is still data to read from the scanner */
s->canceling = SANE_TRUE;
/* XXX check this condition, we used to check
* for SANE_STATUS_CANCELLED */
while (!s->eof &&
(status == SANE_STATUS_GOOD
|| status == SANE_STATUS_DEVICE_BUSY)) {
/* empty body, the while condition does the processing */
/* XXX ? */
status = sane_read(s, dummy,
s->params.bytes_per_line,
&len);
2006-12-02 14:47:21 +00:00
}
free(dummy);
2006-12-02 14:47:21 +00:00
}
}
static void
filter_resolution_list(Epson_Scanner *s)
2006-12-02 14:47:21 +00:00
{
int i, new_size = 0;
struct Epson_Device *dev = s->hw;
SANE_Bool is_correct_resolution = SANE_FALSE;
2006-12-02 14:47:21 +00:00
if (s->val[OPT_LIMIT_RESOLUTION].w == SANE_FALSE) {
2006-12-02 14:47:21 +00:00
/* copy the full list */
dev->resolution_list[0] = dev->res_list_size;
memcpy(&(dev->resolution_list[1]), dev->res_list,
dev->res_list_size * sizeof(SANE_Word));
2006-12-02 14:47:21 +00:00
return;
}
2006-12-02 14:47:21 +00:00
/* shorten the list */
/* filter out all values that are not 300 or 400 dpi based */
for (i = 0; i < dev->res_list_size; i++) {
2006-12-02 14:47:21 +00:00
SANE_Word res = dev->res_list[i];
if ((res < 100) || res == 150 || ((res % 300) == 0)
|| ((res % 400) == 0)) {
/* add the value */
new_size++;
dev->resolution_list[new_size] =
dev->res_list[i];
/* check for a valid current resolution */
if (res == s->val[OPT_RESOLUTION].w)
is_correct_resolution = SANE_TRUE;
2006-12-02 14:47:21 +00:00
}
}
2006-12-02 14:47:21 +00:00
dev->resolution_list[0] = new_size;
/* if the current resolution is invalid, accordingly
* to the filtered list, search for a suitable one
*/
if (is_correct_resolution == SANE_TRUE)
return;
for (i = 1; i <= new_size; i++) {
if (s->val[OPT_RESOLUTION].w < dev->resolution_list[i]) {
s->val[OPT_RESOLUTION].w = dev->resolution_list[i];
break;
}
2006-12-02 14:47:21 +00:00
}
}
/*
* SANE_Status sane_set_io_mode()
*
* not supported - for asynchronous I/O
*/
SANE_Status
sane_set_io_mode(SANE_Handle __sane_unused__ handle,
SANE_Bool __sane_unused__ non_blocking)
2006-12-02 14:47:21 +00:00
{
return SANE_STATUS_UNSUPPORTED;
}
/*
* SANE_Status sane_get_select_fd()
*
* not supported - for asynchronous I/O
*/
SANE_Status
sane_get_select_fd(SANE_Handle __sane_unused__ handle,
SANE_Int __sane_unused__ *fd)
2006-12-02 14:47:21 +00:00
{
return SANE_STATUS_UNSUPPORTED;
}