2015-03-30 21:30:41 +00:00
|
|
|
/*
|
|
|
|
* epsonds-cmd.c - Epson ESC/I-2 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 <ctype.h>
|
|
|
|
#include <unistd.h> /* sleep */
|
|
|
|
|
|
|
|
#include "epsonds.h"
|
|
|
|
#include "epsonds-io.h"
|
|
|
|
#include "epsonds-cmd.h"
|
|
|
|
#include "epsonds-ops.h"
|
2016-06-20 22:43:39 +00:00
|
|
|
#include "epsonds-net.h"
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
esci2_parse_block(char *buf, int len, void *userdata, SANE_Status (*cb)(void *userdata, char *token, int len))
|
|
|
|
{
|
|
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
|
|
SANE_Status delayed_status = SANE_STATUS_GOOD;
|
|
|
|
|
|
|
|
|
|
|
|
char *start = buf;
|
|
|
|
char *end = (buf + len) - 1;
|
|
|
|
|
|
|
|
/* 0 : #
|
|
|
|
* 1-3: param
|
|
|
|
* 4- : data
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
char param[4];
|
|
|
|
|
|
|
|
while (*start != '#' && start < end)
|
|
|
|
start++;
|
|
|
|
|
|
|
|
if (*start != '#')
|
|
|
|
break;
|
|
|
|
|
|
|
|
param[0] = *++start;
|
|
|
|
param[1] = *++start;
|
|
|
|
param[2] = *++start;
|
|
|
|
param[3] = '\0';
|
|
|
|
|
|
|
|
if (strncmp("---", param, 3) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* ugly hack to skip over GMT in RESA */
|
|
|
|
if (strncmp("GMT", param, 3) == 0 && *(start + 5) == 'h') {
|
|
|
|
start = start + 4 + 0x100;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the end of the token */
|
|
|
|
{
|
|
|
|
int tlen;
|
|
|
|
char *next = start;
|
|
|
|
|
|
|
|
while (*next != '#' && *next != 0x00 && next < end)
|
|
|
|
next++;
|
|
|
|
|
|
|
|
tlen = next - start - 1;
|
|
|
|
|
|
|
|
if (cb) {
|
|
|
|
status = cb(userdata, start - 2, tlen);
|
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
delayed_status = status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
start = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (delayed_status != SANE_STATUS_GOOD)
|
|
|
|
return delayed_status;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Bool
|
|
|
|
esci2_check_header(const char *cmd, const char *buf, unsigned int *more)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
*more = 0;
|
|
|
|
|
|
|
|
if (strncmp(cmd, buf, 4) != 0) {
|
|
|
|
|
|
|
|
if (strncmp("UNKN", buf, 4) == 0) {
|
|
|
|
DBG(1, "UNKN reply code received\n");
|
|
|
|
} else if (strncmp("INVD", buf, 4) == 0) {
|
|
|
|
DBG(1, "INVD reply code received\n");
|
|
|
|
} else {
|
|
|
|
DBG(1, "%c%c%c%c, unexpected reply code\n", buf[0], buf[1], buf[2], buf[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* INFOx0000100#.... */
|
|
|
|
|
|
|
|
/* read the answer len */
|
|
|
|
if (buf[4] != 'x') {
|
|
|
|
DBG(1, "unknown type in header: %c\n", buf[4]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = sscanf(&buf[5], "%x#", more);
|
|
|
|
if (err != 1) {
|
|
|
|
DBG(1, "cannot decode length from header\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status esci2_cmd(epsonds_scanner* s,
|
|
|
|
char *cmd, size_t len,
|
|
|
|
char *payload, size_t plen,
|
|
|
|
void *userdata, SANE_Status (*cb)(void *userdata, char *token, int len))
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
unsigned int more;
|
2017-05-05 05:05:30 +00:00
|
|
|
char header[13], rbuf[64]; /* add one more byte for header buffer to correct buffer overflow issue,*/
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
DBG(8, "%s: %4s len %lu, payload len: %lu\n", __func__, cmd, len, plen);
|
|
|
|
|
2016-06-20 22:43:39 +00:00
|
|
|
memset(header, 0x00, sizeof(header));
|
|
|
|
memset(rbuf, 0x00, sizeof(rbuf));
|
|
|
|
|
|
|
|
// extra safety check, will not happen
|
|
|
|
if (len != 12) {
|
|
|
|
DBG(1, "%s: command has wrong size (%lu != 12)\n", __func__, len);
|
2015-03-30 21:30:41 +00:00
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
|
2016-06-20 22:43:39 +00:00
|
|
|
// merge ParameterBlock size
|
|
|
|
sprintf(header, "%4.4sx%07x", cmd, (unsigned int)plen);
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2016-06-20 22:43:39 +00:00
|
|
|
// send RequestBlock, request immediate response if there's no payload
|
|
|
|
status = eds_txrx(s, header, len, rbuf, (plen > 0) ? 0 : 64);
|
2015-03-30 21:30:41 +00:00
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2016-06-20 22:43:39 +00:00
|
|
|
/* send ParameterBlock, request response */
|
|
|
|
if (plen) {
|
|
|
|
|
|
|
|
DBG(8, " %12.12s (%lu)\n", header, plen);
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2016-06-20 22:43:39 +00:00
|
|
|
status = eds_txrx(s, payload, plen, rbuf, 64);
|
2015-03-30 21:30:41 +00:00
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* rxbuf holds the DataHeaderBlock, which should be
|
|
|
|
* parsed to know if we need to read more data
|
|
|
|
*/
|
|
|
|
if (!esci2_check_header(cmd, rbuf, &more)) {
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse the received header block */
|
|
|
|
if (cb) {
|
|
|
|
status = esci2_parse_block(rbuf + 12, 64 - 12, userdata, cb);
|
|
|
|
if (status != SANE_STATUS_GOOD && status != SANE_STATUS_DEVICE_BUSY) {
|
|
|
|
DBG(1, "%s: %4s error while parsing received header\n", __func__, cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* header valid, get the data block if present */
|
|
|
|
if (more) {
|
|
|
|
|
|
|
|
char *pbuf = malloc(more);
|
|
|
|
if (pbuf) {
|
|
|
|
|
2016-06-20 22:43:39 +00:00
|
|
|
if (s->hw->connection == SANE_EPSONDS_NET) {
|
|
|
|
epsonds_net_request_read(s, more);
|
|
|
|
}
|
|
|
|
|
2015-03-30 21:30:41 +00:00
|
|
|
ssize_t read = eds_recv(s, pbuf, more, &status);
|
|
|
|
if (read != more) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse the received data block */
|
|
|
|
if (cb) {
|
|
|
|
status = esci2_parse_block(pbuf, more, userdata, cb);
|
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
DBG(1, "%s: %4s error while parsing received data block\n", __func__, cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(pbuf);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status esci2_cmd_simple(epsonds_scanner* s, char *cmd, SANE_Status (*cb)(void *userdata, char *token, int len))
|
|
|
|
{
|
|
|
|
return esci2_cmd(s, cmd, 12, NULL, 0, s, cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_fin(epsonds_scanner *s)
|
|
|
|
{
|
2015-09-23 11:22:16 +00:00
|
|
|
SANE_Status status;
|
|
|
|
|
2015-04-03 22:49:22 +00:00
|
|
|
DBG(5, "%s\n", __func__);
|
|
|
|
|
2015-09-23 11:22:16 +00:00
|
|
|
status = esci2_cmd_simple(s, "FIN x0000000", NULL);
|
2015-03-30 21:30:41 +00:00
|
|
|
s->locked = 0;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_can(epsonds_scanner *s)
|
|
|
|
{
|
|
|
|
return esci2_cmd_simple(s, "CAN x0000000", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int decode_value(char *buf, int len)
|
|
|
|
{
|
|
|
|
char tmp[10];
|
|
|
|
|
|
|
|
memcpy(tmp, buf, len);
|
|
|
|
tmp[len] = '\0';
|
|
|
|
|
|
|
|
if (buf[0] == 'd' && len == 4) {
|
|
|
|
return strtol(buf + 1, NULL, 10);
|
|
|
|
} else if (buf[0] == 'i' && len == 8) {
|
|
|
|
return strtol(buf + 1, NULL, 10);
|
|
|
|
} else if (buf[0] == 'x' && len == 8) {
|
|
|
|
return strtol(buf + 1, NULL, 16);
|
|
|
|
} else if (buf[0] == 'h' && len == 4) {
|
|
|
|
return strtol(buf + 1, NULL, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* h000 */
|
|
|
|
static char *decode_binary(char *buf)
|
|
|
|
{
|
|
|
|
char tmp[6];
|
2015-09-23 11:22:16 +00:00
|
|
|
int hl;
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
memcpy(tmp, buf, 4);
|
|
|
|
tmp[4] = '\0';
|
|
|
|
|
|
|
|
if (buf[0] != 'h')
|
|
|
|
return NULL;
|
|
|
|
|
2015-09-23 11:22:16 +00:00
|
|
|
hl = strtol(tmp + 1, NULL, 16);
|
2015-03-30 21:30:41 +00:00
|
|
|
if (hl) {
|
|
|
|
|
|
|
|
char *v = malloc(hl + 1);
|
|
|
|
memcpy(v, buf + 4, hl);
|
|
|
|
v[hl] = '\0';
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *decode_string(char *buf)
|
|
|
|
{
|
2015-09-23 11:22:16 +00:00
|
|
|
char *p, *s = decode_binary(buf);
|
2015-03-30 21:30:41 +00:00
|
|
|
if (s == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* trim white space at the end */
|
2015-09-23 11:22:16 +00:00
|
|
|
p = s + strlen(s);
|
2015-03-30 21:30:41 +00:00
|
|
|
while (*--p == ' ')
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void debug_token(int level, const char *func, char *token, int len)
|
|
|
|
{
|
|
|
|
char *tdata = malloc(len + 1);
|
|
|
|
memcpy(tdata, token + 3, len);
|
|
|
|
tdata[len] = '\0';
|
|
|
|
|
|
|
|
DBG(level, "%s: %3.3s / %s / %d\n", func, token, tdata, len);
|
|
|
|
|
|
|
|
free(tdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status info_cb(void *userdata, char *token, int len)
|
|
|
|
{
|
|
|
|
epsonds_scanner *s = (epsonds_scanner *)userdata;
|
2015-09-23 11:22:16 +00:00
|
|
|
char *value;
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
if (DBG_LEVEL >= 11) {
|
|
|
|
debug_token(DBG_LEVEL, __func__, token, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pointer to the token's value */
|
2015-09-23 11:22:16 +00:00
|
|
|
value = token + 3;
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
/* nrd / nrdBUSY */
|
|
|
|
|
|
|
|
if (strncmp("nrd", token, 3) == 0) {
|
|
|
|
if (strncmp("BUSY", value, 4) == 0) {
|
|
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("PRD", token, 3) == 0) {
|
|
|
|
free(s->hw->model);
|
|
|
|
s->hw->model = decode_string(value);
|
|
|
|
s->hw->sane.model = s->hw->model;
|
|
|
|
DBG(1, " product: %s\n", s->hw->model);
|
|
|
|
/* we will free the string later */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("VER", token, 3) == 0) {
|
|
|
|
char *v = decode_string(value);
|
|
|
|
DBG(1, " version: %s\n", v);
|
|
|
|
free(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("S/N", token, 3) == 0) {
|
|
|
|
char *v = decode_string(value);
|
|
|
|
DBG(1, " serial: %s\n", v);
|
|
|
|
free(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ADF", token, 3) == 0) {
|
|
|
|
|
|
|
|
s->hw->has_adf = 1;
|
|
|
|
|
|
|
|
if (len == 8) {
|
|
|
|
|
|
|
|
if (strncmp("TYPEPAGE", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: page type\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("TYPEFEED", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: sheet feed type\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("DPLX1SCN", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: duplex single pass\n");
|
|
|
|
s->hw->adf_singlepass = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("DPLX2SCN", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: duplex double pass\n");
|
|
|
|
s->hw->adf_singlepass = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("FORDPF1N", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: order is 1 to N\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("FORDPFN1", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: order is N to 1\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ALGNLEFT", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: left aligned\n");
|
|
|
|
s->hw->adf_alignment = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ALGNCNTR", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: center aligned\n");
|
|
|
|
s->hw->adf_alignment = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ALGNRIGT", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: right aligned (not supported!)\n");
|
|
|
|
s->hw->adf_alignment = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 4) {
|
|
|
|
|
|
|
|
if (strncmp("PREF", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: auto pre-feed\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ASCN", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: auto scan\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("RCVR", value, len) == 0) {
|
|
|
|
DBG(1, " ADF: auto recovery\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 20) {
|
|
|
|
|
|
|
|
/* ADFAREAi0000850i0001400 */
|
|
|
|
|
|
|
|
if (strncmp("AREA", value, 4) == 0) {
|
|
|
|
|
|
|
|
int min = decode_value(value + 4, 8);
|
|
|
|
int max = decode_value(value + 4 + 8, 8);
|
|
|
|
|
|
|
|
DBG(1, " ADF: area %dx%d @ 100dpi\n", min, max);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("AMIN", value, 4) == 0) {
|
|
|
|
|
|
|
|
int min = decode_value(value + 4, 8);
|
|
|
|
int max = decode_value(value + 4 + 8, 8);
|
|
|
|
|
|
|
|
DBG(1, " ADF: min %dx%d @ 100dpi\n", min, max);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("AMAX", value, 4) == 0) {
|
|
|
|
|
|
|
|
int min = decode_value(value + 4, 8);
|
|
|
|
int max = decode_value(value + 4 + 8, 8);
|
|
|
|
|
|
|
|
DBG(1, " ADF: max %dx%d @ 100dpi\n", min, max);
|
2015-04-08 00:29:20 +00:00
|
|
|
|
|
|
|
eds_set_adf_area(s->hw, min, max, 100);
|
2015-03-30 21:30:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 12) {
|
|
|
|
|
|
|
|
/* RESOi0000600 */
|
|
|
|
|
|
|
|
if (strncmp("RESO", value, 4) == 0) {
|
|
|
|
|
|
|
|
int res = decode_value(value + 4, 8);
|
|
|
|
|
|
|
|
DBG(1, " ADF: basic resolution is %d dpi\n", res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OVSNd025d035 */
|
|
|
|
|
|
|
|
if (strncmp("OVSN", value, 4) == 0) {
|
|
|
|
|
|
|
|
int x = decode_value(value + 4, 4);
|
|
|
|
int y = decode_value(value + 4 + 4, 4);
|
|
|
|
|
|
|
|
DBG(1, " ADF: overscan %dx%d @ 100dpi\n", x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("FB ", token, 3) == 0) {
|
|
|
|
|
|
|
|
s->hw->has_fb = 1;
|
|
|
|
|
|
|
|
if (len == 20) {
|
|
|
|
|
|
|
|
/* AREAi0000850i0001400 */
|
|
|
|
if (strncmp("AREA", value, 4) == 0) {
|
|
|
|
|
|
|
|
int min = decode_value(value + 4, 8);
|
|
|
|
int max = decode_value(value + 4 + 8, 8);
|
|
|
|
|
|
|
|
DBG(1, " FB: area %dx%d @ 100dpi\n", min, max);
|
|
|
|
|
|
|
|
eds_set_fbf_area(s->hw, min, max, 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 8) {
|
|
|
|
|
|
|
|
if (strncmp("ALGNLEFT", value, len) == 0) {
|
|
|
|
DBG(1, " FB: left aligned\n");
|
|
|
|
s->hw->fbf_alignment = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ALGNCNTR", value, len) == 0) {
|
|
|
|
DBG(1, " FB: center aligned\n");
|
|
|
|
s->hw->fbf_alignment = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ALGNRIGT", value, len) == 0) {
|
|
|
|
DBG(1, " FB: right aligned (not supported!)\n");
|
|
|
|
s->hw->fbf_alignment = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 12) {
|
|
|
|
|
|
|
|
/* RESOi0000600 */
|
|
|
|
|
|
|
|
if (strncmp("RESO", value, 4) == 0) {
|
|
|
|
|
|
|
|
int res = decode_value(value + 4, 8);
|
|
|
|
|
|
|
|
DBG(1, " FB: basic resolution is %d dpi\n", res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OVSNd025d035 */
|
|
|
|
|
|
|
|
if (strncmp("OVSN", value, 4) == 0) {
|
|
|
|
|
|
|
|
int x = decode_value(value + 4, 4);
|
|
|
|
int y = decode_value(value + 4 + 4, 4);
|
|
|
|
|
|
|
|
DBG(1, " FB: overscan %dx%d @ 100dpi\n", x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 4) {
|
|
|
|
|
|
|
|
if (strncmp("DETX", value, len) == 0) {
|
|
|
|
DBG(1, " FB: paper width detection\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("DETY", value, len) == 0) {
|
|
|
|
DBG(1, " FB: paper height detection\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_info(epsonds_scanner *s)
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
int i = 4;
|
|
|
|
|
2015-09-23 11:22:16 +00:00
|
|
|
DBG(1, "= gathering device information\n");
|
|
|
|
|
2015-03-30 21:30:41 +00:00
|
|
|
do {
|
|
|
|
status = esci2_cmd_simple(s, "INFOx0000000", &info_cb);
|
|
|
|
if (status == SANE_STATUS_DEVICE_BUSY) {
|
|
|
|
sleep(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
i--;
|
|
|
|
|
|
|
|
} while (status == SANE_STATUS_DEVICE_BUSY && i);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CAPA */
|
|
|
|
|
|
|
|
static SANE_Status capa_cb(void *userdata, char *token, int len)
|
|
|
|
{
|
|
|
|
epsonds_scanner *s = (epsonds_scanner *)userdata;
|
|
|
|
|
|
|
|
char *value = token + 3;
|
|
|
|
|
|
|
|
if (DBG_LEVEL >= 11) {
|
|
|
|
debug_token(DBG_LEVEL, __func__, token, len);
|
|
|
|
}
|
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (len == 4) {
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFDPLX", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: duplex\n");
|
|
|
|
s->hw->adf_is_duplex = 1;
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFSKEW", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: skew correction\n");
|
|
|
|
s->hw->adf_has_skew = 1;
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFOVSN", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: overscan\n");
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFPEDT", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: paper end detection\n");
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFLOAD", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: paper load\n");
|
|
|
|
s->hw->adf_has_load = 1;
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFEJCT", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: paper eject\n");
|
|
|
|
s->hw->adf_has_eject = 1;
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFCRP ", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: image cropping\n");
|
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
|
2015-04-03 23:53:18 +00:00
|
|
|
if (strncmp("ADFFAST", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: fast mode available\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("ADFDFL1", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: double feed detection\n");
|
2015-04-26 21:11:37 +00:00
|
|
|
s->hw->adf_has_dfd = 1;
|
2015-04-03 23:53:18 +00:00
|
|
|
}
|
2015-03-30 21:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 8 && strncmp("ADFDFL1DFL2", token, 3 + 4) == 0) {
|
|
|
|
DBG(1, " ADF: double feed detection (high sensitivity)\n");
|
2015-04-26 21:11:37 +00:00
|
|
|
s->hw->adf_has_dfd = 2;
|
2015-03-30 21:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("FMT", token, 3) == 0) {
|
|
|
|
|
|
|
|
/* a bit ugly... */
|
|
|
|
|
|
|
|
if (len >= 8) {
|
|
|
|
if (strncmp("RAW ", value + 4, 4) == 0) {
|
|
|
|
s->hw->has_raw = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len >= 12) {
|
|
|
|
if (strncmp("RAW ", value + 8, 4) == 0) {
|
|
|
|
s->hw->has_raw = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RSMRANGi0000050i0000600 */
|
|
|
|
|
|
|
|
if (strncmp("RSMRANG", token, 3 + 4) == 0) {
|
|
|
|
|
|
|
|
char *p = token + 3 + 4;
|
|
|
|
|
|
|
|
if (p[0] == 'i') {
|
|
|
|
|
|
|
|
int min = decode_value(p, 8);
|
|
|
|
int max = decode_value(p + 8, 8);
|
|
|
|
|
2015-04-07 23:37:54 +00:00
|
|
|
eds_set_resolution_range(s->hw, min, max);
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
DBG(1, "resolution min/max %d/%d\n", min, max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RSMLISTi0000300i0000600 */
|
|
|
|
|
|
|
|
if (strncmp("RSMLIST", token, 3 + 4) == 0) {
|
|
|
|
|
|
|
|
char *p = token + 3 + 4;
|
|
|
|
|
|
|
|
if (p[0] == 'i') {
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int count = (len - 4) / 8;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
|
|
|
eds_add_resolution(s->hw, decode_value(p, 8));
|
|
|
|
p += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_capa(epsonds_scanner *s)
|
|
|
|
{
|
|
|
|
return esci2_cmd_simple(s, "CAPAx0000000", &capa_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* STAT */
|
|
|
|
|
|
|
|
static SANE_Status stat_cb(void *userdata, char *token, int len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
epsonds_scanner *s = (epsonds_scanner *)userdata;
|
|
|
|
char *value = token + 3;
|
|
|
|
*/
|
|
|
|
userdata = userdata;
|
|
|
|
|
|
|
|
if (DBG_LEVEL >= 11) {
|
|
|
|
debug_token(DBG_LEVEL, __func__, token, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_stat(epsonds_scanner *s)
|
|
|
|
{
|
|
|
|
return esci2_cmd_simple(s, "STATx0000000", &stat_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RESA */
|
|
|
|
|
|
|
|
static SANE_Status resa_cb(void *userdata, char *token, int len)
|
|
|
|
{
|
|
|
|
/* epsonds_scanner *s = (epsonds_scanner *)userdata; */
|
|
|
|
|
|
|
|
userdata = userdata;
|
|
|
|
|
|
|
|
if (DBG_LEVEL >= 11) {
|
|
|
|
debug_token(DBG_LEVEL, __func__, token, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_resa(epsonds_scanner *s)
|
|
|
|
{
|
|
|
|
return esci2_cmd_simple(s, "RESAx0000000", &resa_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PARA */
|
|
|
|
|
|
|
|
static SANE_Status para_cb(void *userdata, char *token, int len)
|
|
|
|
{
|
|
|
|
if (DBG_LEVEL >= 11) {
|
|
|
|
debug_token(DBG_LEVEL, __func__, token, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
userdata = userdata;
|
|
|
|
|
|
|
|
if (strncmp("par", token, 3) == 0) {
|
|
|
|
if (strncmp("FAIL", token + 3, 4) == 0) {
|
|
|
|
DBG(1, "%s: parameter setting failed\n", __func__);
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_para(epsonds_scanner *s, char *parameters)
|
|
|
|
{
|
|
|
|
DBG(8, "%s: %s\n", __func__, parameters);
|
|
|
|
return esci2_cmd(s, "PARAx0000000", 12, parameters, strlen(parameters), NULL, ¶_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_mech(epsonds_scanner *s, char *parameters)
|
|
|
|
{
|
|
|
|
DBG(8, "%s: %s\n", __func__, parameters);
|
|
|
|
return esci2_cmd(s, "MECHx0000000", 12, parameters, strlen(parameters), NULL, ¶_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status esci2_trdt(epsonds_scanner *s)
|
|
|
|
{
|
|
|
|
return esci2_cmd_simple(s, "TRDTx0000000", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static SANE_Status img_cb(void *userdata, char *token, int len)
|
|
|
|
{
|
|
|
|
struct epsonds_scanner *s = userdata;
|
|
|
|
|
|
|
|
if (DBG_LEVEL >= 11) {
|
|
|
|
debug_token(DBG_LEVEL, __func__, token, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* psti0000256i0000000i0000945 / 24 */
|
|
|
|
|
|
|
|
/* integer comparison first so it's faster */
|
|
|
|
if (len == 24 && strncmp("pst", token, 3) == 0) {
|
|
|
|
|
|
|
|
s->dummy = decode_value(token + 3 + 8, 8);
|
|
|
|
|
|
|
|
DBG(10, "%s: pst width: %d, height: %d, dummy: %d\n",
|
|
|
|
__func__,
|
|
|
|
decode_value(token + 3, 8),
|
|
|
|
decode_value(token + 3 + 8 + 8, 8),
|
|
|
|
s->dummy);
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 16 && strncmp("pen", token, 3) == 0) {
|
|
|
|
DBG(10, "%s: page end\n", __func__);
|
|
|
|
s->eof = 1;
|
|
|
|
return SANE_STATUS_EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* typIMGA or typIMGB */
|
|
|
|
if (len == 4 && strncmp("typ", token, 3) == 0) {
|
|
|
|
|
|
|
|
if (token[6] == 'B')
|
|
|
|
s->backside = 1;
|
|
|
|
else
|
|
|
|
s->backside = 0;
|
2015-04-03 23:53:18 +00:00
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
2015-03-30 21:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp("err", token, 3) == 0) {
|
|
|
|
|
|
|
|
char *option = token + 3; /* ADF, TPU, FB */
|
|
|
|
char *cause = token + 3 + 4; /* OPN, PJ, PE, ERR, LTF, LOCK, DFED, DTCL, AUT, PERM */
|
|
|
|
|
2015-09-23 11:22:16 +00:00
|
|
|
s->scanning = 0;
|
|
|
|
|
2015-03-30 21:30:41 +00:00
|
|
|
DBG(1, "%s: error on option %3.3s, cause %4.4s\n",
|
|
|
|
__func__, option, cause);
|
|
|
|
|
|
|
|
if (cause[0] == 'P' && cause[1] == 'J')
|
|
|
|
return SANE_STATUS_JAMMED;
|
|
|
|
|
|
|
|
if (cause[0] == 'P' && cause[1] == 'E')
|
|
|
|
return SANE_STATUS_NO_DOCS;
|
|
|
|
|
|
|
|
if (cause[0] == 'O' && cause[1] == 'P' && cause[2] == 'N')
|
|
|
|
return SANE_STATUS_COVER_OPEN;
|
|
|
|
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 4 && strncmp("atnCAN ", token, 3 + 4) == 0) {
|
2015-04-03 23:53:18 +00:00
|
|
|
DBG(1, "%s: cancel request\n", __func__);
|
2015-03-30 21:30:41 +00:00
|
|
|
s->canceling = 1;
|
|
|
|
s->scanning = 0;
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 4 && strncmp("lftd000", token, 3 + 4) == 0) {
|
|
|
|
s->scanning = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
esci2_img(struct epsonds_scanner *s, SANE_Int *length)
|
|
|
|
{
|
|
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
2015-09-23 11:22:16 +00:00
|
|
|
SANE_Status parse_status;
|
|
|
|
unsigned int more;
|
|
|
|
ssize_t read;
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
*length = 0;
|
|
|
|
|
|
|
|
if (s->canceling)
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
|
|
|
|
|
|
/* request image data */
|
2016-06-20 22:43:39 +00:00
|
|
|
eds_send(s, "IMG x0000000", 12, &status, 64);
|
2015-03-30 21:30:41 +00:00
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* receive DataHeaderBlock */
|
|
|
|
memset(s->buf, 0x00, 64);
|
|
|
|
eds_recv(s, s->buf, 64, &status);
|
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if we need to read any image data */
|
2015-09-23 11:22:16 +00:00
|
|
|
more = 0;
|
2015-03-30 21:30:41 +00:00
|
|
|
if (!esci2_check_header("IMG ", (char *)s->buf, &more)) {
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this handles eof and errors */
|
2015-09-23 11:22:16 +00:00
|
|
|
parse_status = esci2_parse_block((char *)s->buf + 12, 64 - 12, s, &img_cb);
|
2015-03-30 21:30:41 +00:00
|
|
|
|
|
|
|
/* no more data? return using the status of the esci2_parse_block
|
|
|
|
* call, which might hold other error conditions.
|
|
|
|
*/
|
|
|
|
if (!more) {
|
|
|
|
return parse_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ALWAYS read image data */
|
2016-06-20 22:43:39 +00:00
|
|
|
if (s->hw->connection == SANE_EPSONDS_NET) {
|
|
|
|
epsonds_net_request_read(s, more);
|
|
|
|
}
|
|
|
|
|
2015-09-23 11:22:16 +00:00
|
|
|
read = eds_recv(s, s->buf, more, &status);
|
2015-03-30 21:30:41 +00:00
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read != more) {
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle esci2_parse_block errors */
|
|
|
|
if (parse_status != SANE_STATUS_GOOD) {
|
|
|
|
return parse_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG(15, "%s: read %lu bytes, status: %d\n", __func__, (unsigned long) read, status);
|
|
|
|
|
|
|
|
*length = read;
|
|
|
|
|
|
|
|
if (s->canceling) {
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|