2008-12-15 18:31:55 +00:00
|
|
|
/*
|
2016-11-06 18:50:06 +00:00
|
|
|
* SANE backend for Xerox Phaser 3200MFP et al.
|
|
|
|
* Copyright 2008-2016 ABC <abc@telekom.ru>
|
2008-12-15 18:31:55 +00:00
|
|
|
*
|
2016-11-06 18:50:06 +00:00
|
|
|
* Network Scanners Support
|
|
|
|
* Copyright 2010 Alexander Kuznetsov <acca(at)cpan.org>
|
|
|
|
*
|
|
|
|
* Color scanning on Samsung M2870 model and Xerox Cognac 3215 & 3225
|
|
|
|
* models by Laxmeesh Onkar Markod <m.laxmeesh@samsung.com>
|
2010-09-30 11:11:56 +00:00
|
|
|
*
|
2008-12-15 18:31:55 +00:00
|
|
|
* This program is licensed under GPL + SANE exception.
|
|
|
|
* More info at http://www.sane-project.org/license.html
|
|
|
|
*/
|
|
|
|
|
2010-09-30 11:11:56 +00:00
|
|
|
#define DEBUG_NOT_STATIC
|
|
|
|
#define BACKEND_NAME xerox_mfp
|
|
|
|
|
2009-02-25 02:11:17 +00:00
|
|
|
#include "../include/sane/config.h"
|
2008-12-15 18:31:55 +00:00
|
|
|
#include "../include/lassert.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/time.h>
|
2011-10-30 09:32:34 +00:00
|
|
|
#include <sys/types.h>
|
2009-05-08 03:06:20 +00:00
|
|
|
#include "../include/sane/sane.h"
|
|
|
|
#include "../include/sane/sanei.h"
|
|
|
|
#include "../include/sane/saneopts.h"
|
|
|
|
#include "../include/sane/sanei_thread.h"
|
|
|
|
#include "../include/sane/sanei_usb.h"
|
|
|
|
#include "../include/sane/sanei_config.h"
|
|
|
|
#include "../include/sane/sanei_backend.h"
|
2016-06-13 11:35:34 +00:00
|
|
|
#ifdef HAVE_LIBJPEG
|
2016-05-07 19:33:03 +00:00
|
|
|
#include <jpeglib.h>
|
2016-06-13 11:35:34 +00:00
|
|
|
#endif
|
2008-12-15 18:31:55 +00:00
|
|
|
#include "xerox_mfp.h"
|
|
|
|
|
2012-06-02 21:48:45 +00:00
|
|
|
#define BACKEND_BUILD 13
|
2008-12-15 18:31:55 +00:00
|
|
|
#define XEROX_CONFIG_FILE "xerox_mfp.conf"
|
|
|
|
|
|
|
|
static const SANE_Device **devlist = NULL; /* sane_get_devices array */
|
|
|
|
static struct device *devices_head = NULL; /* sane_get_devices list */
|
|
|
|
|
2011-03-08 14:57:19 +00:00
|
|
|
enum { TRANSPORT_USB, TRANSPORT_TCP, TRANSPORTS_MAX };
|
|
|
|
transport available_transports[TRANSPORTS_MAX] = {
|
2010-09-30 11:11:56 +00:00
|
|
|
{ "usb", usb_dev_request, usb_dev_open, usb_dev_close, usb_configure_device },
|
|
|
|
{ "tcp", tcp_dev_request, tcp_dev_open, tcp_dev_close, tcp_configure_device },
|
|
|
|
};
|
2008-12-15 18:31:55 +00:00
|
|
|
|
|
|
|
static int resolv_state(int state)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
if (state & STATE_DOCUMENT_JAM)
|
|
|
|
return SANE_STATUS_JAMMED;
|
|
|
|
if (state & STATE_NO_DOCUMENT)
|
|
|
|
return SANE_STATUS_NO_DOCS;
|
|
|
|
if (state & STATE_COVER_OPEN)
|
|
|
|
return SANE_STATUS_COVER_OPEN;
|
|
|
|
if (state & STATE_INVALID_AREA)
|
|
|
|
return SANE_STATUS_INVAL; /* sane_start: implies SANE_INFO_RELOAD_OPTIONS */
|
|
|
|
if (state & STATE_WARMING)
|
2009-04-12 19:40:31 +00:00
|
|
|
#ifdef SANE_STATUS_WARMING_UP
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_WARMING_UP;
|
2009-04-12 19:40:31 +00:00
|
|
|
#else
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_DEVICE_BUSY;
|
2009-04-12 19:40:31 +00:00
|
|
|
#endif
|
2016-11-06 17:24:02 +00:00
|
|
|
if (state & STATE_LOCKING)
|
2009-04-12 19:40:31 +00:00
|
|
|
#ifdef SANE_STATUS_HW_LOCKED
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_HW_LOCKED;
|
2009-04-12 19:40:31 +00:00
|
|
|
#else
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_JAMMED;
|
2009-04-12 19:40:31 +00:00
|
|
|
#endif
|
2016-11-06 17:24:02 +00:00
|
|
|
if (state & ~STATE_NO_ERROR)
|
|
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
return 0;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *str_cmd(int cmd)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
switch (cmd) {
|
2008-12-15 18:31:55 +00:00
|
|
|
case CMD_ABORT: return "ABORT";
|
|
|
|
case CMD_INQUIRY: return "INQUIRY";
|
|
|
|
case CMD_RESERVE_UNIT: return "RESERVE_UNIT";
|
|
|
|
case CMD_RELEASE_UNIT: return "RELEASE_UNIT";
|
|
|
|
case CMD_SET_WINDOW: return "SET_WINDOW";
|
|
|
|
case CMD_READ: return "READ";
|
|
|
|
case CMD_READ_IMAGE: return "READ_IMAGE";
|
|
|
|
case CMD_OBJECT_POSITION: return "OBJECT_POSITION";
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
return "unknown";
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_DUMP 70
|
2016-05-07 19:33:03 +00:00
|
|
|
const char *encTmpFileName = "/tmp/stmp_enc.tmp";
|
|
|
|
|
2019-11-05 19:01:53 +00:00
|
|
|
/*
|
|
|
|
* Decode jpeg from `infilename` into dev->decData of dev->decDataSize size.
|
|
|
|
*/
|
2016-09-25 06:44:36 +00:00
|
|
|
static int decompress(struct device __sane_unused__ *dev,
|
|
|
|
const char __sane_unused__ *infilename)
|
2016-05-07 19:33:03 +00:00
|
|
|
{
|
2016-06-13 11:35:34 +00:00
|
|
|
#ifdef HAVE_LIBJPEG
|
2016-11-06 17:24:02 +00:00
|
|
|
int rc;
|
|
|
|
int row_stride, width, height, pixel_size;
|
|
|
|
struct jpeg_decompress_struct cinfo;
|
|
|
|
struct jpeg_error_mgr jerr;
|
|
|
|
unsigned long bmp_size = 0;
|
|
|
|
FILE *pInfile = NULL;
|
|
|
|
JSAMPARRAY buffer;
|
|
|
|
|
|
|
|
if ((pInfile = fopen(infilename, "rb")) == NULL) {
|
|
|
|
fprintf(stderr, "can't open %s\n", infilename);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
|
|
|
|
jpeg_stdio_src(&cinfo, pInfile);
|
|
|
|
|
|
|
|
rc = jpeg_read_header(&cinfo, TRUE);
|
|
|
|
if (rc != 1) {
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
fclose(pInfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
|
|
|
|
width = cinfo.output_width;
|
|
|
|
height = cinfo.output_height;
|
|
|
|
pixel_size = cinfo.output_components;
|
|
|
|
bmp_size = width * height * pixel_size;
|
2019-11-05 19:01:53 +00:00
|
|
|
assert(bmp_size <= POST_DATASIZE);
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->decDataSize = bmp_size;
|
|
|
|
|
|
|
|
row_stride = width * pixel_size;
|
|
|
|
|
|
|
|
buffer = (*cinfo.mem->alloc_sarray)
|
|
|
|
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
|
|
|
|
|
|
|
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
|
|
buffer[0] = dev->decData + \
|
|
|
|
(cinfo.output_scanline) * row_stride;
|
|
|
|
jpeg_read_scanlines(&cinfo, buffer, 1);
|
|
|
|
}
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
fclose(pInfile);
|
|
|
|
return 0;
|
2016-06-13 11:35:34 +00:00
|
|
|
#else
|
2016-11-06 17:24:02 +00:00
|
|
|
return -1;
|
2016-06-13 11:35:34 +00:00
|
|
|
#endif
|
2016-05-07 19:33:03 +00:00
|
|
|
}
|
|
|
|
|
2019-11-05 19:01:53 +00:00
|
|
|
/* copy from decoded jpeg image (dev->decData) into user's buffer (pDest) */
|
|
|
|
/* returns 0 if there is no data to copy */
|
2016-05-07 19:33:03 +00:00
|
|
|
static int copy_decompress_data(struct device *dev, unsigned char *pDest, int maxlen, int *destLen)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int data_size = 0;
|
|
|
|
|
2019-11-05 19:01:53 +00:00
|
|
|
if (destLen)
|
|
|
|
*destLen = 0;
|
|
|
|
if (!dev->decDataSize)
|
|
|
|
return 0;
|
2016-11-06 17:24:02 +00:00
|
|
|
data_size = dev->decDataSize - dev->currentDecDataIndex;
|
2019-11-05 19:01:53 +00:00
|
|
|
if (data_size > maxlen)
|
2016-11-06 17:24:02 +00:00
|
|
|
data_size = maxlen;
|
2019-11-05 19:01:53 +00:00
|
|
|
if (data_size && pDest) {
|
|
|
|
memcpy(pDest, dev->decData + dev->currentDecDataIndex, data_size);
|
|
|
|
if (destLen)
|
|
|
|
*destLen = data_size;
|
|
|
|
dev->currentDecDataIndex += data_size;
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
if (dev->decDataSize == dev->currentDecDataIndex) {
|
|
|
|
dev->currentDecDataIndex = 0;
|
|
|
|
dev->decDataSize = 0;
|
|
|
|
}
|
2019-11-05 19:01:53 +00:00
|
|
|
return 1;
|
2016-05-07 19:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int decompress_tempfile(struct device *dev)
|
|
|
|
{
|
|
|
|
decompress(dev, encTmpFileName);
|
2016-11-06 17:24:02 +00:00
|
|
|
remove(encTmpFileName);
|
|
|
|
return 0;
|
2016-05-07 19:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_to_tmp_file(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
unsigned char *pSrc = dev->data;
|
|
|
|
int srcLen = dev->datalen;
|
|
|
|
FILE *pInfile;
|
|
|
|
if ((pInfile = fopen(encTmpFileName, "a")) == NULL) {
|
|
|
|
fprintf(stderr, "can't open %s\n", encTmpFileName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fwrite(pSrc, 1, srcLen, pInfile);
|
|
|
|
fclose(pInfile);
|
|
|
|
return srcLen;
|
2016-05-07 19:33:03 +00:00
|
|
|
}
|
|
|
|
|
2016-09-25 06:44:36 +00:00
|
|
|
static int isSupportedDevice(struct device __sane_unused__ *dev)
|
2016-05-07 19:33:03 +00:00
|
|
|
{
|
2016-06-13 11:35:34 +00:00
|
|
|
#ifdef HAVE_LIBJPEG
|
2016-11-06 17:24:02 +00:00
|
|
|
/* Checking device which supports JPEG Lossy compression for color scanning*/
|
2018-05-17 22:31:46 +00:00
|
|
|
if (dev->compressionTypes & (1 << 6)) {
|
2023-02-21 00:48:34 +00:00
|
|
|
/* blacklist malfunctioning device(s) */
|
|
|
|
if (!strncmp (dev->sane.model, "SCX-4500W", 9)
|
|
|
|
|| !strncmp (dev->sane.model, "C460", 4)
|
2024-01-25 22:45:51 +00:00
|
|
|
|| !!strstr(dev->sane.model, "SCX-472")
|
2023-02-21 00:48:34 +00:00
|
|
|
|| !!strstr (dev->sane.model, "WorkCentre 3225")
|
|
|
|
|| !!strstr (dev->sane.model, "CLX-3170")
|
|
|
|
|| !!strstr (dev->sane.model, "4x24")
|
|
|
|
|| !!strstr (dev->sane.model, "4x28")
|
|
|
|
|| !strncmp (dev->sane.model, "M288x", 5))
|
|
|
|
return 0;
|
2016-11-06 17:24:02 +00:00
|
|
|
return 1;
|
2018-05-17 22:31:46 +00:00
|
|
|
} else
|
2016-11-06 17:24:02 +00:00
|
|
|
return 0;
|
2016-06-13 11:35:34 +00:00
|
|
|
#else
|
2016-11-06 17:24:02 +00:00
|
|
|
return 0;
|
2016-06-13 11:35:34 +00:00
|
|
|
#endif
|
2016-05-07 19:33:03 +00:00
|
|
|
}
|
|
|
|
|
2021-03-30 11:07:24 +00:00
|
|
|
static int isJPEGEnabled(struct device __sane_unused__ *dev)
|
|
|
|
{
|
|
|
|
return isSupportedDevice(dev) && dev->compressionEnabled;
|
|
|
|
}
|
|
|
|
|
2008-12-15 18:31:55 +00:00
|
|
|
static void dbg_dump(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int i;
|
|
|
|
char dbuf[MAX_DUMP * 3 + 1], *dptr = dbuf;
|
|
|
|
int nzlen = dev->reslen;
|
|
|
|
int dlen = MIN(dev->reslen, MAX_DUMP);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
for (i = dev->reslen - 1; i >= 0; i--, nzlen--)
|
|
|
|
if (dev->res[i] != 0)
|
|
|
|
break;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dlen = MIN(dlen, nzlen + 1);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
for (i = 0; i < dlen; i++, dptr += 3)
|
|
|
|
sprintf(dptr, " %02x", dev->res[i]);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(5, "[%zu]%s%s\n", dev->reslen, dbuf,
|
2016-11-06 17:24:02 +00:00
|
|
|
(dlen < (int)dev->reslen)? "..." : "");
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* one command to device */
|
|
|
|
/* return 0: on error, 1: success */
|
2016-11-06 17:24:02 +00:00
|
|
|
static int dev_command(struct device *dev, SANE_Byte *cmd, size_t reqlen)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_Status status;
|
|
|
|
size_t sendlen = cmd[3] + 4;
|
|
|
|
SANE_Byte *res = dev->res;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
assert(reqlen <= sizeof(dev->res)); /* requested len */
|
|
|
|
dev->reslen = sizeof(dev->res); /* doing full buffer to flush stalled commands */
|
|
|
|
|
|
|
|
if (cmd[2] == CMD_SET_WINDOW) {
|
|
|
|
/* Set Window have wrong packet length, huh. */
|
|
|
|
sendlen = 25;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (cmd[2] == CMD_READ_IMAGE) {
|
|
|
|
/* Read Image is raw data, don't need to read response */
|
|
|
|
res = NULL;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->state = 0;
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(4, ":: dev_command(%s[%#x], %zu)\n", str_cmd(cmd[2]), cmd[2],
|
|
|
|
reqlen);
|
2016-11-06 17:24:02 +00:00
|
|
|
status = dev->io->dev_request(dev, cmd, sendlen, res, &dev->reslen);
|
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
DBG(1, "%s: dev_request: %s\n", __func__, sane_strstatus(status));
|
|
|
|
dev->state = SANE_STATUS_IO_ERROR;
|
|
|
|
return 0;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
/* if not need response just return success */
|
|
|
|
return 1;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
/* normal command reply, some sanity checking */
|
|
|
|
if (dev->reslen < reqlen) {
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(1, "%s: illegal response len %zu, need %zu\n",
|
|
|
|
__func__, dev->reslen, reqlen);
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->state = SANE_STATUS_IO_ERROR;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
size_t pktlen; /* len specified in packet */
|
|
|
|
|
|
|
|
if (DBG_LEVEL > 3)
|
|
|
|
dbg_dump(dev);
|
|
|
|
|
|
|
|
if (dev->res[0] != RES_CODE) {
|
|
|
|
DBG(2, "%s: illegal data header %02x\n", __func__, dev->res[0]);
|
|
|
|
dev->state = SANE_STATUS_IO_ERROR;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
pktlen = dev->res[2] + 3;
|
|
|
|
if (dev->reslen != pktlen) {
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(2, "%s: illegal response len %zu, should be %zu\n",
|
|
|
|
__func__, pktlen, dev->reslen);
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->state = SANE_STATUS_IO_ERROR;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (dev->reslen > reqlen)
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(2, "%s: too big packet len %zu, need %zu\n",
|
|
|
|
__func__, dev->reslen, reqlen);
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dev->state = 0;
|
|
|
|
if (cmd[2] == CMD_SET_WINDOW ||
|
|
|
|
cmd[2] == CMD_OBJECT_POSITION ||
|
|
|
|
cmd[2] == CMD_READ ||
|
|
|
|
cmd[2] == CMD_RESERVE_UNIT) {
|
|
|
|
if (dev->res[1] == STATUS_BUSY)
|
|
|
|
dev->state = SANE_STATUS_DEVICE_BUSY;
|
|
|
|
else if (dev->res[1] == STATUS_CANCEL)
|
|
|
|
dev->state = SANE_STATUS_CANCELLED;
|
|
|
|
else if (dev->res[1] == STATUS_CHECK)
|
|
|
|
dev->state = resolv_state((cmd[2] == CMD_READ)?
|
|
|
|
(dev->res[12] << 8 | dev->res[13]) :
|
|
|
|
(dev->res[4] << 8 | dev->res[5]));
|
|
|
|
|
|
|
|
if (dev->state)
|
|
|
|
DBG(3, "%s(%s[%#x]): => %d: %s\n",
|
|
|
|
__func__, str_cmd(cmd[2]), cmd[2],
|
|
|
|
dev->state, sane_strstatus(dev->state));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* one short command to device */
|
2016-11-06 17:24:02 +00:00
|
|
|
static int dev_cmd(struct device *dev, SANE_Byte command)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_Byte cmd[4] = { REQ_CODE_A, REQ_CODE_B };
|
|
|
|
cmd[2] = command;
|
|
|
|
return dev_command(dev, cmd, (command == CMD_INQUIRY)? 70 : 32);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* stop scanning operation. return previous status */
|
|
|
|
static SANE_Status dev_stop(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int state = dev->state;
|
|
|
|
|
|
|
|
DBG(3, "%s: %p, scanning %d, reserved %d\n", __func__,
|
|
|
|
(void *)dev, dev->scanning, dev->reserved);
|
|
|
|
dev->scanning = 0;
|
|
|
|
|
|
|
|
/* release */
|
|
|
|
if (!dev->reserved)
|
|
|
|
return state;
|
|
|
|
dev->reserved = 0;
|
|
|
|
dev_cmd(dev, CMD_RELEASE_UNIT);
|
|
|
|
DBG(3, "total image %d*%d size %d (win %d*%d), %d*%d %d data: %d, out %d bytes\n",
|
|
|
|
dev->para.pixels_per_line, dev->para.lines,
|
|
|
|
dev->total_img_size,
|
|
|
|
dev->win_width, dev->win_len,
|
|
|
|
dev->pixels_per_line, dev->ulines, dev->blocks,
|
|
|
|
dev->total_data_size, dev->total_out_size);
|
|
|
|
dev->state = state;
|
|
|
|
return state;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2010-09-30 11:11:56 +00:00
|
|
|
SANE_Status ret_cancel(struct device *dev, SANE_Status ret)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
dev_cmd(dev, CMD_ABORT);
|
|
|
|
if (dev->scanning) {
|
|
|
|
dev_stop(dev);
|
|
|
|
dev->state = SANE_STATUS_CANCELLED;
|
|
|
|
}
|
|
|
|
return ret;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cancelled(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
if (dev->cancel)
|
|
|
|
return ret_cancel(dev, 1);
|
|
|
|
return 0;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* issue command and wait until scanner is not busy */
|
|
|
|
/* return 0 on error/blocking, 1 is ok and ready */
|
|
|
|
static int dev_cmd_wait(struct device *dev, int cmd)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int sleeptime = 10;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (cancelled(dev))
|
|
|
|
return 0;
|
|
|
|
if (!dev_cmd(dev, cmd)) {
|
|
|
|
dev->state = SANE_STATUS_IO_ERROR;
|
|
|
|
return 0;
|
|
|
|
} else if (dev->state) {
|
|
|
|
if (dev->state != SANE_STATUS_DEVICE_BUSY)
|
|
|
|
return 0;
|
|
|
|
else {
|
|
|
|
if (dev->non_blocking) {
|
|
|
|
dev->state = SANE_STATUS_GOOD;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if (sleeptime > 1000)
|
|
|
|
sleeptime = 1000;
|
|
|
|
DBG(4, "(%s) sleeping(%d ms).. [%x %x]\n",
|
|
|
|
str_cmd(cmd), sleeptime, dev->res[4], dev->res[5]);
|
|
|
|
usleep(sleeptime * 1000);
|
|
|
|
if (sleeptime < 1000)
|
|
|
|
sleeptime *= (sleeptime < 100)? 10 : 2;
|
|
|
|
}
|
|
|
|
} /* BUSY */
|
|
|
|
}
|
|
|
|
} while (dev->state == SANE_STATUS_DEVICE_BUSY);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
return 1;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int inq_dpi_bits[] = {
|
2016-11-06 17:24:02 +00:00
|
|
|
75, 150, 0, 0,
|
|
|
|
200, 300, 0, 0,
|
|
|
|
600, 0, 0, 1200,
|
|
|
|
100, 0, 0, 2400,
|
|
|
|
0, 4800, 0, 9600
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int res_dpi_codes[] = {
|
2016-11-06 17:24:02 +00:00
|
|
|
75, 0, 150, 0,
|
|
|
|
0, 300, 0, 600,
|
|
|
|
1200, 200, 100, 2400,
|
|
|
|
4800, 9600
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
static int SANE_Word_sort(const void *a, const void *b)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
return *(const SANE_Word *)a - *(const SANE_Word *)b;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* resolve inquired dpi list to dpi_list array */
|
|
|
|
static void resolv_inq_dpi(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
unsigned int i;
|
|
|
|
int res = dev->resolutions;
|
|
|
|
|
|
|
|
assert(sizeof(inq_dpi_bits) < sizeof(dev->dpi_list));
|
|
|
|
for (i = 0; i < sizeof(inq_dpi_bits) / sizeof(int); i++)
|
|
|
|
if (inq_dpi_bits[i] && (res & (1 << i)))
|
|
|
|
dev->dpi_list[++dev->dpi_list[0]] = inq_dpi_bits[i];
|
|
|
|
qsort(&dev->dpi_list[1], dev->dpi_list[0], sizeof(SANE_Word), SANE_Word_sort);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int dpi_to_code(int dpi)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
unsigned int i;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
for (i = 0; i < sizeof(res_dpi_codes) / sizeof(int); i++) {
|
|
|
|
if (dpi == res_dpi_codes[i])
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return 0;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int string_match_index(const SANE_String_Const s[], SANE_String m)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; *s; i++) {
|
|
|
|
SANE_String_Const x = *s++;
|
|
|
|
if (strcasecmp(x, m) == 0)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return 0;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_String string_match(const SANE_String_Const s[], SANE_String m)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
return UNCONST(s[string_match_index(s, m)]);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
static size_t max_string_size(SANE_String_Const s[])
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
size_t max = 0;
|
|
|
|
|
|
|
|
while (*s) {
|
|
|
|
size_t size = strlen(*s++) + 1;
|
|
|
|
if (size > max)
|
|
|
|
max = size;
|
|
|
|
}
|
|
|
|
return max;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_String_Const doc_sources[] = {
|
2016-11-06 17:24:02 +00:00
|
|
|
"Flatbed", "ADF", "Auto", NULL
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int doc_source_to_code[] = {
|
2024-01-25 22:51:31 +00:00
|
|
|
DOC_FLATBED, DOC_ADF, DOC_AUTO
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static SANE_String_Const scan_modes[] = {
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_VALUE_SCAN_MODE_LINEART,
|
|
|
|
SANE_VALUE_SCAN_MODE_HALFTONE,
|
|
|
|
SANE_VALUE_SCAN_MODE_GRAY,
|
|
|
|
SANE_VALUE_SCAN_MODE_COLOR,
|
|
|
|
NULL
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int scan_mode_to_code[] = {
|
2016-11-06 17:24:02 +00:00
|
|
|
0x00, 0x01, 0x03, 0x05
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static SANE_Range threshold = {
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_FIX(30), SANE_FIX(70), SANE_FIX(10)
|
2008-12-15 18:31:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void reset_options(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->val[OPT_RESOLUTION].w = 150;
|
|
|
|
dev->val[OPT_MODE].s = string_match(scan_modes, SANE_VALUE_SCAN_MODE_COLOR);
|
|
|
|
|
2020-08-29 06:43:03 +00:00
|
|
|
/* if docs loaded in adf use it as default source, flatbed otherwise */
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->val[OPT_SOURCE].s = UNCONST(doc_sources[(dev->doc_loaded)? 1 : 0]);
|
|
|
|
|
|
|
|
dev->val[OPT_THRESHOLD].w = SANE_FIX(50);
|
|
|
|
|
|
|
|
/* this is reported maximum window size, will be fixed later */
|
|
|
|
dev->win_x_range.min = SANE_FIX(0);
|
|
|
|
dev->win_x_range.max = SANE_FIX((double)dev->max_win_width / PNT_PER_MM);
|
|
|
|
dev->win_x_range.quant = SANE_FIX(1);
|
|
|
|
dev->win_y_range.min = SANE_FIX(0);
|
|
|
|
dev->win_y_range.max = SANE_FIX((double)dev->max_win_len / PNT_PER_MM);
|
|
|
|
dev->win_y_range.quant = SANE_FIX(1);
|
|
|
|
dev->val[OPT_SCAN_TL_X].w = dev->win_x_range.min;
|
|
|
|
dev->val[OPT_SCAN_TL_Y].w = dev->win_y_range.min;
|
|
|
|
dev->val[OPT_SCAN_BR_X].w = dev->win_x_range.max;
|
|
|
|
dev->val[OPT_SCAN_BR_Y].w = dev->win_y_range.max;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void init_options(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_OPTIONS; i++) {
|
|
|
|
dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
|
|
dev->opt[i].size = sizeof(SANE_Word);
|
|
|
|
dev->opt[i].type = SANE_TYPE_FIXED;
|
|
|
|
dev->val[i].s = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->opt[OPT_NUMOPTIONS].name = SANE_NAME_NUM_OPTIONS;
|
|
|
|
dev->opt[OPT_NUMOPTIONS].title = SANE_TITLE_NUM_OPTIONS;
|
|
|
|
dev->opt[OPT_NUMOPTIONS].desc = SANE_DESC_NUM_OPTIONS;
|
|
|
|
dev->opt[OPT_NUMOPTIONS].type = SANE_TYPE_INT;
|
|
|
|
dev->opt[OPT_NUMOPTIONS].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
dev->val[OPT_NUMOPTIONS].w = NUM_OPTIONS;
|
|
|
|
|
|
|
|
dev->opt[OPT_GROUP_STD].name = SANE_NAME_STANDARD;
|
|
|
|
dev->opt[OPT_GROUP_STD].title = SANE_TITLE_STANDARD;
|
|
|
|
dev->opt[OPT_GROUP_STD].desc = SANE_DESC_STANDARD;
|
|
|
|
dev->opt[OPT_GROUP_STD].type = SANE_TYPE_GROUP;
|
|
|
|
dev->opt[OPT_GROUP_STD].cap = 0;
|
|
|
|
|
|
|
|
dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
|
|
|
|
dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
|
|
|
|
dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
|
|
|
|
dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
|
|
|
|
dev->opt[OPT_RESOLUTION].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
|
|
|
|
dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
|
|
|
|
dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
|
|
dev->opt[OPT_RESOLUTION].constraint.word_list = dev->dpi_list;
|
|
|
|
|
|
|
|
dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
|
|
|
|
dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
|
|
|
|
dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
|
|
|
|
dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
|
|
|
|
dev->opt[OPT_MODE].size = max_string_size(scan_modes);
|
|
|
|
dev->opt[OPT_MODE].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
|
|
|
|
dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
dev->opt[OPT_MODE].constraint.string_list = scan_modes;
|
|
|
|
|
|
|
|
dev->opt[OPT_THRESHOLD].name = SANE_NAME_HIGHLIGHT;
|
|
|
|
dev->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
|
|
|
|
dev->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
|
|
|
|
dev->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
|
|
|
|
dev->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
|
|
|
|
dev->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
dev->opt[OPT_THRESHOLD].constraint.range = &threshold;
|
|
|
|
|
|
|
|
dev->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
|
|
|
|
dev->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
|
|
|
|
dev->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
|
|
|
|
dev->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
|
|
|
|
dev->opt[OPT_SOURCE].size = max_string_size(doc_sources);
|
|
|
|
dev->opt[OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT;
|
|
|
|
dev->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
|
|
dev->opt[OPT_SOURCE].constraint.string_list = doc_sources;
|
|
|
|
|
2021-03-30 11:07:24 +00:00
|
|
|
dev->opt[OPT_JPEG].name = "jpeg";
|
|
|
|
dev->opt[OPT_JPEG].title = SANE_I18N("jpeg compression");
|
|
|
|
dev->opt[OPT_JPEG].desc = SANE_I18N("JPEG Image Compression");
|
|
|
|
dev->opt[OPT_JPEG].unit = SANE_UNIT_NONE;
|
|
|
|
dev->opt[OPT_JPEG].type = SANE_TYPE_BOOL;
|
|
|
|
dev->opt[OPT_JPEG].cap |= SANE_CAP_ADVANCED;
|
|
|
|
#ifdef HAVE_LIBJPEG
|
|
|
|
dev->compressionEnabled = SANE_TRUE;
|
|
|
|
if (!isSupportedDevice(dev))
|
|
|
|
dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
|
|
|
|
dev->val[OPT_JPEG].b = SANE_TRUE;
|
|
|
|
#else
|
|
|
|
dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
|
|
|
|
dev->val[OPT_JPEG].b = SANE_FALSE;
|
|
|
|
#endif
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->opt[OPT_GROUP_GEO].name = SANE_NAME_GEOMETRY;
|
|
|
|
dev->opt[OPT_GROUP_GEO].title = SANE_TITLE_GEOMETRY;
|
|
|
|
dev->opt[OPT_GROUP_GEO].desc = SANE_DESC_GEOMETRY;
|
|
|
|
dev->opt[OPT_GROUP_GEO].type = SANE_TYPE_GROUP;
|
|
|
|
dev->opt[OPT_GROUP_GEO].cap = 0;
|
|
|
|
|
|
|
|
dev->opt[OPT_SCAN_TL_X].name = SANE_NAME_SCAN_TL_X;
|
|
|
|
dev->opt[OPT_SCAN_TL_X].title = SANE_TITLE_SCAN_TL_X;
|
|
|
|
dev->opt[OPT_SCAN_TL_X].desc = SANE_DESC_SCAN_TL_X;
|
|
|
|
dev->opt[OPT_SCAN_TL_X].unit = SANE_UNIT_MM;
|
|
|
|
dev->opt[OPT_SCAN_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
dev->opt[OPT_SCAN_TL_X].constraint.range = &dev->win_x_range;
|
|
|
|
|
|
|
|
dev->opt[OPT_SCAN_TL_Y].name = SANE_NAME_SCAN_TL_Y;
|
|
|
|
dev->opt[OPT_SCAN_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
|
|
|
|
dev->opt[OPT_SCAN_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
|
|
|
|
dev->opt[OPT_SCAN_TL_Y].unit = SANE_UNIT_MM;
|
|
|
|
dev->opt[OPT_SCAN_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
dev->opt[OPT_SCAN_TL_Y].constraint.range = &dev->win_y_range;
|
|
|
|
|
|
|
|
dev->opt[OPT_SCAN_BR_X].name = SANE_NAME_SCAN_BR_X;
|
|
|
|
dev->opt[OPT_SCAN_BR_X].title = SANE_TITLE_SCAN_BR_X;
|
|
|
|
dev->opt[OPT_SCAN_BR_X].desc = SANE_DESC_SCAN_BR_X;
|
|
|
|
dev->opt[OPT_SCAN_BR_X].unit = SANE_UNIT_MM;
|
|
|
|
dev->opt[OPT_SCAN_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
dev->opt[OPT_SCAN_BR_X].constraint.range = &dev->win_x_range;
|
|
|
|
|
|
|
|
dev->opt[OPT_SCAN_BR_Y].name = SANE_NAME_SCAN_BR_Y;
|
|
|
|
dev->opt[OPT_SCAN_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
|
|
|
|
dev->opt[OPT_SCAN_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
|
|
|
|
dev->opt[OPT_SCAN_BR_Y].unit = SANE_UNIT_MM;
|
|
|
|
dev->opt[OPT_SCAN_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
dev->opt[OPT_SCAN_BR_Y].constraint.range = &dev->win_y_range;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2009-02-02 18:29:08 +00:00
|
|
|
/* fill parameters from options */
|
|
|
|
static void set_parameters(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
double px_to_len;
|
2009-02-02 18:29:08 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->para.last_frame = SANE_TRUE;
|
|
|
|
dev->para.lines = -1;
|
|
|
|
px_to_len = 1200.0 / dev->val[OPT_RESOLUTION].w;
|
2009-02-02 18:29:08 +00:00
|
|
|
#define BETTER_BASEDPI 1
|
2016-11-06 17:24:02 +00:00
|
|
|
/* tests prove that 1200dpi base is very inexact
|
|
|
|
* so I calculated better values for each axis */
|
|
|
|
#if BETTER_BASEDPI
|
|
|
|
px_to_len = 1180.0 / dev->val[OPT_RESOLUTION].w;
|
2009-02-02 18:29:08 +00:00
|
|
|
#endif
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->para.pixels_per_line = dev->win_width / px_to_len;
|
|
|
|
dev->para.bytes_per_line = dev->para.pixels_per_line;
|
2016-05-07 19:33:03 +00:00
|
|
|
|
2021-03-30 11:07:24 +00:00
|
|
|
DBG(5, dev->val[OPT_JPEG].b ? "JPEG compression enabled\n" : "JPEG compression disabled\n" );
|
|
|
|
dev->compressionEnabled = dev->val[OPT_JPEG].b;
|
|
|
|
|
|
|
|
if (!isJPEGEnabled(dev)) {
|
2009-02-02 18:29:08 +00:00
|
|
|
#if BETTER_BASEDPI
|
2016-11-06 17:24:02 +00:00
|
|
|
px_to_len = 1213.9 / dev->val[OPT_RESOLUTION].w;
|
2009-02-02 18:29:08 +00:00
|
|
|
#endif
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
dev->para.lines = dev->win_len / px_to_len;
|
|
|
|
if (dev->composition == MODE_LINEART ||
|
|
|
|
dev->composition == MODE_HALFTONE) {
|
|
|
|
dev->para.format = SANE_FRAME_GRAY;
|
|
|
|
dev->para.depth = 1;
|
|
|
|
dev->para.bytes_per_line = (dev->para.pixels_per_line + 7) / 8;
|
|
|
|
} else if (dev->composition == MODE_GRAY8) {
|
|
|
|
dev->para.format = SANE_FRAME_GRAY;
|
|
|
|
dev->para.depth = 8;
|
|
|
|
dev->para.bytes_per_line = dev->para.pixels_per_line;
|
|
|
|
} else if (dev->composition == MODE_RGB24) {
|
|
|
|
dev->para.format = SANE_FRAME_RGB;
|
|
|
|
dev->para.depth = 8;
|
|
|
|
dev->para.bytes_per_line *= 3;
|
|
|
|
} else {
|
|
|
|
/* this will never happen */
|
|
|
|
DBG(1, "%s: impossible image composition %d\n",
|
|
|
|
__func__, dev->composition);
|
|
|
|
dev->para.format = SANE_FRAME_GRAY;
|
|
|
|
dev->para.depth = 8;
|
|
|
|
}
|
2009-02-02 18:29:08 +00:00
|
|
|
}
|
|
|
|
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
/* determine if document is to be sourced from ADF */
|
|
|
|
static int sourcing_from_adf(struct device *dev)
|
|
|
|
{
|
|
|
|
return (dev->doc_source == DOC_ADF ||
|
|
|
|
(dev->doc_source == DOC_AUTO && dev->doc_loaded));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-15 18:31:55 +00:00
|
|
|
/* resolve all options related to scan window */
|
|
|
|
/* called after option changed and in set_window */
|
|
|
|
static int fix_window(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
double win_width_mm, win_len_mm;
|
|
|
|
int i;
|
|
|
|
int threshold = SANE_UNFIX(dev->val[OPT_THRESHOLD].w);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->resolution = dpi_to_code(dev->val[OPT_RESOLUTION].w);
|
|
|
|
dev->composition = scan_mode_to_code[string_match_index(scan_modes, dev->val[OPT_MODE].s)];
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (dev->composition == MODE_LINEART ||
|
|
|
|
dev->composition == MODE_HALFTONE) {
|
|
|
|
dev->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
} else {
|
|
|
|
dev->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
|
|
if (threshold < 30) {
|
|
|
|
dev->val[OPT_THRESHOLD].w = SANE_FIX(30);
|
|
|
|
} else if (threshold > 70) {
|
|
|
|
dev->val[OPT_THRESHOLD].w = SANE_FIX(70);
|
|
|
|
}
|
|
|
|
threshold = SANE_UNFIX(dev->val[OPT_THRESHOLD].w);
|
|
|
|
dev->threshold = (threshold - 30) / 10;
|
|
|
|
dev->val[OPT_THRESHOLD].w = SANE_FIX(dev->threshold * 10 + 30);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->doc_source = doc_source_to_code[string_match_index(doc_sources, dev->val[OPT_SOURCE].s)];
|
|
|
|
|
|
|
|
/* max window len is dependent of document source */
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
if (sourcing_from_adf(dev))
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->max_len = dev->max_len_adf;
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
else
|
|
|
|
dev->max_len = dev->max_len_fb;
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
/* parameters */
|
|
|
|
dev->win_y_range.max = SANE_FIX((double)dev->max_len / PNT_PER_MM);
|
|
|
|
|
|
|
|
/* window sanity checking */
|
|
|
|
for (i = OPT_SCAN_TL_X; i <= OPT_SCAN_BR_Y; i++) {
|
|
|
|
if (dev->val[i].w < dev->opt[i].constraint.range->min)
|
|
|
|
dev->val[i].w = dev->opt[i].constraint.range->min;
|
|
|
|
if (dev->val[i].w > dev->opt[i].constraint.range->max)
|
|
|
|
dev->val[i].w = dev->opt[i].constraint.range->max;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->val[OPT_SCAN_TL_X].w > dev->val[OPT_SCAN_BR_X].w)
|
|
|
|
SWAP_Word(dev->val[OPT_SCAN_TL_X].w, dev->val[OPT_SCAN_BR_X].w);
|
|
|
|
if (dev->val[OPT_SCAN_TL_Y].w > dev->val[OPT_SCAN_BR_Y].w)
|
|
|
|
SWAP_Word(dev->val[OPT_SCAN_TL_Y].w, dev->val[OPT_SCAN_BR_Y].w);
|
|
|
|
|
|
|
|
/* recalculate millimeters to inches */
|
|
|
|
dev->win_off_x = SANE_UNFIX(dev->val[OPT_SCAN_TL_X].w) / MM_PER_INCH;
|
|
|
|
dev->win_off_y = SANE_UNFIX(dev->val[OPT_SCAN_TL_Y].w) / MM_PER_INCH;
|
|
|
|
|
|
|
|
/* calc win size in mm */
|
|
|
|
win_width_mm = SANE_UNFIX(dev->val[OPT_SCAN_BR_X].w) -
|
|
|
|
SANE_UNFIX(dev->val[OPT_SCAN_TL_X].w);
|
|
|
|
win_len_mm = SANE_UNFIX(dev->val[OPT_SCAN_BR_Y].w) -
|
|
|
|
SANE_UNFIX(dev->val[OPT_SCAN_TL_Y].w);
|
|
|
|
/* convert mm to 1200 dpi points */
|
|
|
|
dev->win_width = (int)(win_width_mm * PNT_PER_MM);
|
|
|
|
dev->win_len = (int)(win_len_mm * PNT_PER_MM);
|
|
|
|
|
|
|
|
/* don't scan if window is zero size */
|
|
|
|
if (!dev->win_width || !dev->win_len) {
|
|
|
|
/* "The scan cannot be started with the current set of options." */
|
|
|
|
dev->state = SANE_STATUS_INVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dev_set_window(struct device *dev)
|
|
|
|
{
|
|
|
|
SANE_Byte cmd[0x19] = {
|
|
|
|
REQ_CODE_A, REQ_CODE_B, CMD_SET_WINDOW, 0x13, MSG_SCANNING_PARAM
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!fix_window(dev))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cmd[0x05] = dev->win_width >> 24;
|
|
|
|
cmd[0x06] = dev->win_width >> 16;
|
|
|
|
cmd[0x07] = dev->win_width >> 8;
|
|
|
|
cmd[0x08] = dev->win_width;
|
|
|
|
cmd[0x09] = dev->win_len >> 24;
|
|
|
|
cmd[0x0a] = dev->win_len >> 16;
|
|
|
|
cmd[0x0b] = dev->win_len >> 8;
|
|
|
|
cmd[0x0c] = dev->win_len;
|
|
|
|
cmd[0x0d] = dev->resolution; /* x */
|
|
|
|
cmd[0x0e] = dev->resolution; /* y */
|
|
|
|
cmd[0x0f] = (SANE_Byte)floor(dev->win_off_x);
|
|
|
|
cmd[0x10] = (SANE_Byte)((dev->win_off_x - floor(dev->win_off_x)) * 100);
|
|
|
|
cmd[0x11] = (SANE_Byte)floor(dev->win_off_y);
|
|
|
|
cmd[0x12] = (SANE_Byte)((dev->win_off_y - floor(dev->win_off_y)) * 100);
|
|
|
|
cmd[0x13] = dev->composition;
|
|
|
|
/* Set to JPEG Lossy Compression, if mode is color (only for supported model)...
|
|
|
|
* else go with Uncompressed (For backard compatibility with old models )*/
|
|
|
|
if (dev->composition == MODE_RGB24) {
|
2021-03-30 11:07:24 +00:00
|
|
|
if (isJPEGEnabled(dev)) {
|
2016-11-06 17:24:02 +00:00
|
|
|
cmd[0x14] = 0x6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd[0x16] = dev->threshold;
|
|
|
|
cmd[0x17] = dev->doc_source;
|
|
|
|
|
|
|
|
DBG(5, "OFF xi: %02x%02x yi: %02x%02x,"
|
|
|
|
" WIN xp: %02x%02x%02x%02x yp %02x%02x%02x%02x,"
|
|
|
|
" MAX %08x %08x\n",
|
|
|
|
cmd[0x0f], cmd[0x10], cmd[0x11], cmd[0x12],
|
|
|
|
cmd[0x05], cmd[0x06], cmd[0x07], cmd[0x08],
|
|
|
|
cmd[0x09], cmd[0x0a], cmd[0x0b], cmd[0x0c],
|
|
|
|
dev->max_win_width, dev->max_win_len);
|
|
|
|
|
|
|
|
return dev_command(dev, cmd, 32);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
dev_inquiry(struct device *dev)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_Byte *ptr;
|
|
|
|
SANE_Char *optr, *xptr;
|
|
|
|
|
|
|
|
if (!dev_cmd(dev, CMD_INQUIRY))
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
ptr = dev->res;
|
|
|
|
if (ptr[3] != MSG_PRODUCT_INFO) {
|
|
|
|
DBG(1, "%s: illegal INQUIRY response %02x\n", __func__, ptr[3]);
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse reported manufacturer/product names */
|
|
|
|
dev->sane.vendor = optr = (SANE_Char *) malloc(33);
|
|
|
|
for (ptr += 4; ptr < &dev->res[0x24] && *ptr && *ptr != ' ';)
|
|
|
|
*optr++ = *ptr++;
|
|
|
|
*optr++ = 0;
|
|
|
|
|
|
|
|
for (; ptr < &dev->res[0x24] && (!*ptr || *ptr == ' '); ptr++)
|
|
|
|
/* skip spaces */;
|
|
|
|
|
|
|
|
dev->sane.model = optr = (SANE_Char *) malloc(33);
|
|
|
|
xptr = optr; /* is last non space character + 1 */
|
|
|
|
for (; ptr < &dev->res[0x24] && *ptr;) {
|
|
|
|
if (*ptr != ' ')
|
|
|
|
xptr = optr + 1;
|
|
|
|
*optr++ = *ptr++;
|
|
|
|
}
|
|
|
|
*optr++ = 0;
|
|
|
|
*xptr = 0;
|
|
|
|
|
|
|
|
DBG(1, "%s: found %s/%s\n", __func__, dev->sane.vendor, dev->sane.model);
|
|
|
|
dev->sane.type = strdup("multi-function peripheral");
|
|
|
|
|
|
|
|
dev->resolutions = dev->res[0x37] << 16 |
|
|
|
|
dev->res[0x24] << 8 |
|
|
|
|
dev->res[0x25];
|
|
|
|
dev->compositions = dev->res[0x27];
|
|
|
|
dev->max_win_width = dev->res[0x28] << 24 |
|
|
|
|
dev->res[0x29] << 16 |
|
|
|
|
dev->res[0x2a] << 8 |
|
|
|
|
dev->res[0x2b];
|
|
|
|
dev->max_win_len = dev->res[0x2c] << 24 |
|
|
|
|
dev->res[0x2d] << 16 |
|
|
|
|
dev->res[0x2e] << 8 |
|
|
|
|
dev->res[0x2f];
|
|
|
|
dev->max_len_adf = dev->res[0x38] << 24 |
|
|
|
|
dev->res[0x39] << 16 |
|
|
|
|
dev->res[0x3a] << 8 |
|
|
|
|
dev->res[0x3b];
|
|
|
|
dev->max_len_fb = dev->res[0x3c] << 24 |
|
|
|
|
dev->res[0x3d] << 16 |
|
|
|
|
dev->res[0x3e] << 8 |
|
|
|
|
dev->res[0x3f];
|
|
|
|
dev->line_order = dev->res[0x31];
|
|
|
|
dev->compressionTypes = dev->res[0x32];
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
dev->has_adf = ((dev->res[0x26] & 0x03) != 0);
|
|
|
|
dev->doc_loaded = (dev->res[0x35] == 0x02)
|
|
|
|
&& dev->has_adf;
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
init_options(dev);
|
|
|
|
reset_options(dev);
|
|
|
|
fix_window(dev);
|
|
|
|
set_parameters(dev);
|
|
|
|
resolv_inq_dpi(dev);
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
dev_inquiry_adf_status(struct device *dev)
|
|
|
|
{
|
|
|
|
if (!dev_cmd(dev, CMD_INQUIRY))
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
|
|
|
|
dev->has_adf = ((dev->res[0x26] & 0x03) != 0);
|
|
|
|
dev->doc_loaded = (dev->res[0x35] == 0x02)
|
|
|
|
&& dev->has_adf;
|
|
|
|
|
|
|
|
DBG(3, "%s: ADF present: %s, loaded: %s\n", __func__,
|
|
|
|
(dev->has_adf ? "true" : "false"),
|
|
|
|
(dev->doc_loaded ? "true" : "false"));
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-15 18:31:55 +00:00
|
|
|
const SANE_Option_Descriptor *
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_get_option_descriptor(SANE_Handle h, SANE_Int opt)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p, %d\n", __func__, h, opt);
|
|
|
|
if (opt >= NUM_OPTIONS || opt < 0)
|
|
|
|
return NULL;
|
|
|
|
return &dev->opt[opt];
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_control_option(SANE_Handle h, SANE_Int opt, SANE_Action act,
|
|
|
|
void *val, SANE_Word *info)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
|
|
|
|
|
|
|
DBG(3, "%s: %p, %d, <%d>, %p, %p\n", __func__, h, opt, act, val, (void *)info);
|
|
|
|
if (!dev || opt >= NUM_OPTIONS || opt < 0)
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
|
|
if (info)
|
|
|
|
*info = 0;
|
|
|
|
|
|
|
|
if (act == SANE_ACTION_GET_VALUE) { /* GET */
|
|
|
|
if (dev->opt[opt].type == SANE_TYPE_STRING)
|
|
|
|
strcpy(val, dev->val[opt].s);
|
|
|
|
else
|
|
|
|
*(SANE_Word *)val = dev->val[opt].w;
|
|
|
|
} else if (act == SANE_ACTION_SET_VALUE) { /* SET */
|
|
|
|
SANE_Parameters xpara = dev->para;
|
|
|
|
SANE_Option_Descriptor xopt[NUM_OPTIONS];
|
|
|
|
Option_Value xval[NUM_OPTIONS];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (dev->opt[opt].constraint_type == SANE_CONSTRAINT_STRING_LIST) {
|
|
|
|
dev->val[opt].s = string_match(dev->opt[opt].constraint.string_list, val);
|
|
|
|
if (info && strcasecmp(dev->val[opt].s, val))
|
|
|
|
*info |= SANE_INFO_INEXACT;
|
|
|
|
} else if (opt == OPT_RESOLUTION)
|
|
|
|
dev->val[opt].w = res_dpi_codes[dpi_to_code(*(SANE_Word *)val)];
|
|
|
|
else
|
|
|
|
dev->val[opt].w = *(SANE_Word *)val;
|
|
|
|
|
|
|
|
memcpy(&xopt, &dev->opt, sizeof(xopt));
|
|
|
|
memcpy(&xval, &dev->val, sizeof(xval));
|
|
|
|
fix_window(dev);
|
|
|
|
set_parameters(dev);
|
|
|
|
|
|
|
|
/* check for side effects */
|
|
|
|
if (info) {
|
|
|
|
if (memcmp(&xpara, &dev->para, sizeof(xpara)))
|
|
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
|
|
if (memcmp(&xopt, &dev->opt, sizeof(xopt)))
|
|
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
for (i = 0; i < NUM_OPTIONS; i++)
|
|
|
|
if (xval[i].w != dev->val[i].w) {
|
|
|
|
if (i == opt)
|
|
|
|
*info |= SANE_INFO_INEXACT;
|
|
|
|
else
|
|
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
|
|
}
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(4, "%s: %d, <%d> => %08x, %x\n", __func__, opt, act,
|
|
|
|
val? *(SANE_Word *)val : 0, info? *info : 0);
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-11-06 17:24:02 +00:00
|
|
|
dev_free(struct device *dev)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (dev->sane.name)
|
|
|
|
free(UNCONST(dev->sane.name));
|
|
|
|
if (dev->sane.vendor)
|
|
|
|
free(UNCONST(dev->sane.vendor));
|
|
|
|
if (dev->sane.model)
|
|
|
|
free(UNCONST(dev->sane.model));
|
|
|
|
if (dev->sane.type)
|
|
|
|
free(UNCONST(dev->sane.type));
|
|
|
|
if (dev->data)
|
|
|
|
free(dev->data);
|
|
|
|
if (dev->decData) {
|
|
|
|
free(dev->decData);
|
|
|
|
dev->decData = NULL;
|
|
|
|
}
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
|
|
free(dev);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-11-06 17:24:02 +00:00
|
|
|
free_devices(void)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *next;
|
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
if (devlist) {
|
|
|
|
free(devlist);
|
|
|
|
devlist = NULL;
|
|
|
|
}
|
|
|
|
for (dev = devices_head; dev; dev = next) {
|
|
|
|
next = dev->next;
|
|
|
|
dev_free(dev);
|
|
|
|
}
|
|
|
|
devices_head = NULL;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2011-03-08 14:57:19 +00:00
|
|
|
static transport *tr_from_devname(SANE_String_Const devname)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
if (strncmp("tcp", devname, 3) == 0)
|
|
|
|
return &available_transports[TRANSPORT_TCP];
|
|
|
|
return &available_transports[TRANSPORT_USB];
|
2011-03-08 14:57:19 +00:00
|
|
|
}
|
|
|
|
|
2008-12-15 18:31:55 +00:00
|
|
|
static SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
list_one_device(SANE_String_Const devname)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev;
|
|
|
|
SANE_Status status;
|
|
|
|
transport *tr;
|
|
|
|
|
|
|
|
DBG(4, "%s: %s\n", __func__, devname);
|
|
|
|
|
|
|
|
for (dev = devices_head; dev; dev = dev->next) {
|
|
|
|
if (strcmp(dev->sane.name, devname) == 0)
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
tr = tr_from_devname(devname);
|
|
|
|
|
|
|
|
dev = calloc(1, sizeof(struct device));
|
|
|
|
if (dev == NULL)
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
|
|
|
|
dev->sane.name = strdup(devname);
|
|
|
|
dev->io = tr;
|
|
|
|
status = tr->dev_open(dev);
|
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
dev_free(dev);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* status = dev_cmd (dev, CMD_ABORT);*/
|
|
|
|
status = dev_inquiry(dev);
|
|
|
|
tr->dev_close(dev);
|
|
|
|
if (status != SANE_STATUS_GOOD) {
|
|
|
|
DBG(1, "%s: dev_inquiry(%s): %s\n", __func__,
|
|
|
|
dev->sane.name, sane_strstatus(status));
|
|
|
|
dev_free(dev);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* good device, add it to list */
|
|
|
|
dev->next = devices_head;
|
|
|
|
devices_head = dev;
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SANE API ignores return code of this callback */
|
|
|
|
static SANE_Status
|
2020-08-12 10:29:36 +00:00
|
|
|
list_conf_devices(SANEI_Config __sane_unused__ *config, const char *devname,
|
|
|
|
void __sane_unused__ *data)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
return tr_from_devname(devname)->configure_device(devname, list_one_device);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_init(SANE_Int *version_code, SANE_Auth_Callback cb)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG_INIT();
|
|
|
|
DBG(2, "sane_init: Xerox backend (build %d), version %s null, authorize %s null\n", BACKEND_BUILD,
|
|
|
|
(version_code) ? "!=" : "==", (cb) ? "!=" : "==");
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (version_code)
|
2021-06-08 15:13:51 +00:00
|
|
|
*version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BACKEND_BUILD);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
sanei_usb_init();
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_exit(void)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
for (dev = devices_head; dev; dev = dev->next)
|
|
|
|
if (dev->dn != -1)
|
|
|
|
sane_close(dev); /* implies flush */
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
free_devices();
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_get_devices(const SANE_Device *** device_list, SANE_Bool local)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
SANEI_Config config;
|
|
|
|
struct device *dev;
|
|
|
|
int dev_count;
|
|
|
|
int i;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p, %d\n", __func__, (const void *)device_list, local);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (devlist) {
|
|
|
|
if (device_list)
|
|
|
|
*device_list = devlist;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
free_devices();
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
config.count = 0;
|
|
|
|
config.descriptors = NULL;
|
|
|
|
config.values = NULL;
|
2020-08-12 10:29:36 +00:00
|
|
|
sanei_configure_attach(XEROX_CONFIG_FILE, &config, list_conf_devices, NULL);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
for (dev_count = 0, dev = devices_head; dev; dev = dev->next)
|
|
|
|
dev_count++;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
devlist = malloc((dev_count + 1) * sizeof(*devlist));
|
|
|
|
if (!devlist) {
|
|
|
|
DBG(1, "%s: malloc: no memory\n", __func__);
|
|
|
|
return SANE_STATUS_NO_MEM;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
for (i = 0, dev = devices_head; dev; dev = dev->next)
|
|
|
|
devlist[i++] = &dev->sane;
|
|
|
|
devlist[i++] = NULL;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (device_list)
|
|
|
|
*device_list = devlist;
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_close(SANE_Handle h)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev)
|
|
|
|
return;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p (%s)\n", __func__, (void *)dev, dev->sane.name);
|
|
|
|
dev->io->dev_close(dev);
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_open(SANE_String_Const name, SANE_Handle *h)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: '%s'\n", __func__, name);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!devlist)
|
|
|
|
sane_get_devices(NULL, SANE_TRUE);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!name || !*name) {
|
|
|
|
/* special case of empty name: open first available device */
|
|
|
|
for (dev = devices_head; dev; dev = dev->next) {
|
|
|
|
if (dev->dn != -1) {
|
|
|
|
if (sane_open(dev->sane.name, h) == SANE_STATUS_GOOD)
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (dev = devices_head; dev; dev = dev->next) {
|
|
|
|
if (strcmp(name, dev->sane.name) == 0) {
|
|
|
|
*h = dev;
|
|
|
|
return dev->io->dev_open(dev);
|
|
|
|
}
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_INVAL;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_get_parameters(SANE_Handle h, SANE_Parameters *para)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p, %p\n", __func__, h, (void *)para);
|
|
|
|
if (!para)
|
|
|
|
return SANE_STATUS_INVAL;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
*para = dev->para;
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check if image data is ready, and wait if not */
|
|
|
|
/* 1: image is acquired, 0: error or non_blocking mode */
|
|
|
|
static int dev_acquire(struct device *dev)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev_cmd_wait(dev, CMD_READ))
|
2021-03-30 11:16:08 +00:00
|
|
|
return 0;
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
dev->state = SANE_STATUS_GOOD;
|
|
|
|
dev->vertical = dev->res[0x08] << 8 | dev->res[0x09];
|
|
|
|
dev->horizontal = dev->res[0x0a] << 8 | dev->res[0x0b];
|
|
|
|
dev->blocklen = dev->res[4] << 24 |
|
|
|
|
dev->res[5] << 16 |
|
|
|
|
dev->res[6] << 8 |
|
|
|
|
dev->res[7];
|
|
|
|
dev->final_block = (dev->res[3] == MSG_END_BLOCK)? 1 : 0;
|
|
|
|
|
|
|
|
dev->pixels_per_line = dev->horizontal;
|
|
|
|
dev->bytes_per_line = dev->horizontal;
|
|
|
|
|
|
|
|
if (dev->composition == MODE_RGB24)
|
|
|
|
dev->bytes_per_line *= 3;
|
|
|
|
else if (dev->composition == MODE_LINEART ||
|
|
|
|
dev->composition == MODE_HALFTONE)
|
|
|
|
dev->pixels_per_line *= 8;
|
|
|
|
|
|
|
|
DBG(4, "acquiring, size per band v: %d, h: %d, %sblock: %d, slack: %d\n",
|
|
|
|
dev->vertical, dev->horizontal, dev->final_block? "last " : "",
|
|
|
|
dev->blocklen, dev->blocklen - (dev->vertical * dev->bytes_per_line));
|
|
|
|
|
|
|
|
if (dev->bytes_per_line > DATASIZE) {
|
|
|
|
DBG(1, "%s: unsupported line size: %d bytes > %d\n",
|
|
|
|
__func__, dev->bytes_per_line, DATASIZE);
|
2021-03-30 11:16:08 +00:00
|
|
|
ret_cancel(dev, SANE_STATUS_NO_MEM);
|
|
|
|
return 0;
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dev->reading = 0; /* need to issue READ_IMAGE */
|
|
|
|
|
|
|
|
dev->dataindex = 0;
|
|
|
|
dev->datalen = 0;
|
|
|
|
dev->dataoff = 0;
|
|
|
|
|
|
|
|
return 1;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fill_slack(struct device *dev, SANE_Byte *buf, int maxlen)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
const int slack = dev->total_img_size - dev->total_out_size;
|
|
|
|
const int havelen = MIN(slack, maxlen);
|
|
|
|
int j;
|
|
|
|
|
|
|
|
if (havelen <= 0)
|
|
|
|
return 0;
|
|
|
|
for (j = 0; j < havelen; j++)
|
|
|
|
buf[j] = 255;
|
|
|
|
return havelen;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_plain_trim(struct device *dev, SANE_Byte *buf, int maxlen, int *olenp)
|
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
int j;
|
|
|
|
const int linesize = dev->bytes_per_line;
|
|
|
|
int k = dev->dataindex;
|
|
|
|
*olenp = 0;
|
|
|
|
for (j = 0; j < dev->datalen && *olenp < maxlen; j++, k++) {
|
|
|
|
const int x = k % linesize;
|
|
|
|
const int y = k / linesize;
|
|
|
|
if (y >= dev->vertical)
|
|
|
|
break; /* slack */
|
|
|
|
if (x < dev->para.bytes_per_line &&
|
|
|
|
(y + dev->y_off) < dev->para.lines) {
|
|
|
|
*buf++ = dev->data[(dev->dataoff + j) & DATAMASK];
|
|
|
|
(*olenp)++;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->dataindex = k;
|
|
|
|
return j;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* return: how much data could be freed from cyclic buffer */
|
|
|
|
/* convert from RRGGBB to RGBRGB */
|
2016-11-06 17:24:02 +00:00
|
|
|
static int copy_mix_bands_trim(struct device *dev, SANE_Byte *buf, int maxlen, int *olenp)
|
|
|
|
{
|
|
|
|
int j;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
const int linesize = dev->bytes_per_line; /* caching real line size */
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
/* line number of the head of input buffer,
|
|
|
|
* input buffer is always aligned to whole line */
|
|
|
|
const int y_off = dev->dataindex / linesize;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
int k = dev->dataindex; /* caching current index of input buffer */
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
/* can only copy as much as full lines we have */
|
|
|
|
int havelen = dev->datalen / linesize * linesize - k % linesize;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
const int bands = 3;
|
|
|
|
*olenp = 0;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
/* while we have data && they can receive */
|
|
|
|
for (j = 0; j < havelen && *olenp < maxlen; j++, k++) {
|
|
|
|
const int band = (k % bands) * dev->horizontal;
|
|
|
|
const int x = k % linesize / bands;
|
|
|
|
const int y = k / linesize - y_off; /* y relative to buffer head */
|
|
|
|
const int y_rly = y + y_off + dev->y_off; /* global y */
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (x < dev->para.pixels_per_line &&
|
|
|
|
y_rly < dev->para.lines) {
|
|
|
|
*buf++ = dev->data[(dev->dataoff + band + x + y * linesize) & DATAMASK];
|
|
|
|
(*olenp)++;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->dataindex = k;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
/* how much full lines are finished */
|
|
|
|
return (k / linesize - y_off) * linesize;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_Status status;
|
|
|
|
struct device *dev = h;
|
|
|
|
|
2022-03-09 00:00:00 +00:00
|
|
|
DBG(3, "%s: %p, %p, %d, %p\n", __func__, h, (void *) buf, maxlen, (void *) lenp);
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
if (lenp)
|
|
|
|
*lenp = 0;
|
|
|
|
if (!dev)
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
|
|
|
|
if (!dev->scanning)
|
|
|
|
return SANE_STATUS_EOF;
|
|
|
|
|
|
|
|
/* if there is no data to read or output from buffer */
|
2017-01-15 17:59:06 +00:00
|
|
|
if (!dev->blocklen && (dev->datalen <= PADDING_SIZE || dev->final_block)) {
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
/* copying uncompressed data */
|
|
|
|
if (dev->composition == MODE_RGB24 &&
|
2021-03-30 11:07:24 +00:00
|
|
|
isJPEGEnabled(dev) &&
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->decDataSize > 0) {
|
|
|
|
int diff = dev->total_img_size - dev->total_out_size;
|
|
|
|
int bufLen = (diff < maxlen) ? diff : maxlen;
|
2019-11-05 19:01:53 +00:00
|
|
|
if (diff &&
|
|
|
|
copy_decompress_data(dev, buf, bufLen, lenp)) {
|
|
|
|
if (lenp)
|
|
|
|
dev->total_out_size += *lenp;
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
2017-01-15 17:59:17 +00:00
|
|
|
} else if (dev->composition != MODE_RGB24) {
|
|
|
|
int diff = dev->total_img_size - dev->total_out_size;
|
|
|
|
int bufLen = (diff < maxlen) ? diff : maxlen;
|
|
|
|
if (diff > 0 && copy_plain_trim(dev, buf, bufLen, lenp) > 0) {
|
|
|
|
dev->total_out_size += *lenp;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
/* and we don't need to acquire next block */
|
|
|
|
if (dev->final_block) {
|
|
|
|
int slack = dev->total_img_size - dev->total_out_size;
|
|
|
|
|
|
|
|
/* but we may need to fill slack */
|
|
|
|
if (buf && lenp && slack > 0) {
|
|
|
|
*lenp = fill_slack(dev, buf, maxlen);
|
|
|
|
dev->total_out_size += *lenp;
|
|
|
|
DBG(9, "<> slack: %d, filled: %d, maxlen %d\n",
|
|
|
|
slack, *lenp, maxlen);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
} else if (slack < 0) {
|
|
|
|
/* this will never happen */
|
|
|
|
DBG(1, "image overflow %d bytes\n", dev->total_img_size - dev->total_out_size);
|
|
|
|
}
|
2021-03-30 11:07:24 +00:00
|
|
|
if (isJPEGEnabled(dev) &&
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->composition == MODE_RGB24) {
|
|
|
|
remove(encTmpFileName);
|
|
|
|
}
|
|
|
|
/* that's all */
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
/* finished receving the document; */
|
|
|
|
/* stop and release the unit, unless sourcing from ADF */
|
|
|
|
if (!sourcing_from_adf(dev))
|
|
|
|
dev_stop(dev);
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_EOF;
|
2016-05-07 19:33:03 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
/* queue next image block */
|
|
|
|
if (!dev_acquire(dev))
|
|
|
|
return dev->state;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev->reading) {
|
|
|
|
if (cancelled(dev))
|
|
|
|
return dev->state;
|
|
|
|
DBG(5, "READ_IMAGE\n");
|
|
|
|
if (!dev_cmd(dev, CMD_READ_IMAGE))
|
|
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
dev->reading++;
|
|
|
|
dev->ulines += dev->vertical;
|
|
|
|
dev->y_off = dev->ulines - dev->vertical;
|
|
|
|
dev->total_data_size += dev->blocklen;
|
|
|
|
dev->blocks++;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
do {
|
|
|
|
size_t datalen;
|
|
|
|
int clrlen; /* cleared lines len */
|
|
|
|
int olen; /* output len */
|
|
|
|
|
|
|
|
/* read as much data into the buffer */
|
2017-01-15 17:59:06 +00:00
|
|
|
datalen = MIN(dev->blocklen, DATAROOM(dev) & USB_BLOCK_MASK);
|
2016-11-06 17:24:02 +00:00
|
|
|
while (datalen && dev->blocklen) {
|
|
|
|
SANE_Byte *rbuf = dev->data + DATATAIL(dev);
|
|
|
|
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(9, "<> request len: %zu, [%d, %d; %d]\n",
|
|
|
|
datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
|
2017-01-15 17:59:06 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if ((status = dev->io->dev_request(dev, NULL, 0, rbuf, &datalen)) !=
|
|
|
|
SANE_STATUS_GOOD)
|
|
|
|
return status;
|
2017-01-15 17:59:06 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->datalen += datalen;
|
|
|
|
dev->blocklen -= datalen;
|
2017-01-15 17:59:06 +00:00
|
|
|
|
2017-01-15 17:59:14 +00:00
|
|
|
DBG(9, "<> got %zu, [%d, %d; %d]\n",
|
|
|
|
datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
|
2017-01-15 17:59:06 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (dev->blocklen < 0)
|
|
|
|
return ret_cancel(dev, SANE_STATUS_IO_ERROR);
|
|
|
|
|
2017-01-15 17:59:06 +00:00
|
|
|
datalen = MIN(dev->blocklen, DATAROOM(dev) & USB_BLOCK_MASK);
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (buf && lenp) { /* read mode */
|
|
|
|
/* copy will do minimal of valid data */
|
|
|
|
if (dev->para.format == SANE_FRAME_RGB && dev->line_order) {
|
2021-03-30 11:07:24 +00:00
|
|
|
if (isJPEGEnabled(dev)) {
|
2016-11-06 17:24:02 +00:00
|
|
|
clrlen = dump_to_tmp_file(dev);
|
|
|
|
/* decompress after reading entire block data*/
|
|
|
|
if (0 == dev->blocklen) {
|
|
|
|
decompress_tempfile(dev);
|
|
|
|
}
|
|
|
|
copy_decompress_data(dev, buf, maxlen, &olen);
|
|
|
|
} else {
|
|
|
|
clrlen = copy_mix_bands_trim(dev, buf, maxlen, &olen);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
clrlen = copy_plain_trim(dev, buf, maxlen, &olen);
|
|
|
|
|
|
|
|
dev->datalen -= clrlen;
|
|
|
|
dev->dataoff = (dev->dataoff + clrlen) & DATAMASK;
|
|
|
|
buf += olen;
|
|
|
|
maxlen -= olen;
|
|
|
|
*lenp += olen;
|
|
|
|
dev->total_out_size += olen;
|
|
|
|
|
|
|
|
DBG(9, "<> olen: %d, clrlen: %d, blocklen: %d/%d, maxlen %d (%d %d %d)\n",
|
|
|
|
olen, clrlen, dev->blocklen, dev->datalen, maxlen,
|
|
|
|
dev->dataindex / dev->bytes_per_line + dev->y_off,
|
|
|
|
dev->y_off, dev->para.lines);
|
|
|
|
|
|
|
|
/* slack beyond last line */
|
|
|
|
if (dev->dataindex / dev->bytes_per_line + dev->y_off >= dev->para.lines) {
|
|
|
|
dev->datalen = 0;
|
|
|
|
dev->dataoff = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!clrlen || maxlen <= 0)
|
|
|
|
break;
|
|
|
|
} else { /* flush mode */
|
|
|
|
dev->datalen = 0;
|
|
|
|
dev->dataoff = 0;
|
|
|
|
}
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
} while (dev->blocklen);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (lenp)
|
|
|
|
DBG(9, " ==> %d\n", *lenp);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
2016-11-06 17:24:02 +00:00
|
|
|
sane_start(SANE_Handle h)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
|
|
|
DBG(3, "%s: %p\n", __func__, h);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->cancel = 0;
|
|
|
|
dev->scanning = 0;
|
|
|
|
dev->total_img_size = 0;
|
|
|
|
dev->total_out_size = 0;
|
|
|
|
dev->total_data_size = 0;
|
|
|
|
dev->blocks = 0;
|
2016-05-07 19:33:03 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev->reserved) {
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
if (dev->has_adf
|
|
|
|
&& (dev->doc_source == DOC_AUTO || dev->doc_source == DOC_ADF)) {
|
|
|
|
if (dev_inquiry_adf_status(dev) != SANE_STATUS_GOOD)
|
|
|
|
return dev_stop(dev);
|
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev_cmd_wait(dev, CMD_RESERVE_UNIT))
|
|
|
|
return dev->state;
|
|
|
|
dev->reserved++;
|
|
|
|
|
xerox_mfp: When scanning from ADF, do not stop the device at page completion
- Stopping and releasing the device causes the ADF to purge any remaining pages,
thus only the first page gets properly scanned (observed with Samsung
SCX-4729FW).
- Instead, when sourcing from the ADF, at each page's completion it should
continue and issue "OBJECT POSITION" command to proceed to the next page.
- Once all pages have been processed, the subsequent "OBJECT POSITION" command
returns the proper "CHECK CONDITION" status (ADF is empty) and triggers the
device stop and release.
- Additionally, the status of the ADF (loaded/empty) needs to be requeried
at the `sane_start()` for proper handling of the "Auto" mode. Otherwise,
the scanning session retains the ADF state from the start of the session,
even though ADF will become empty. Thus the "Auto" mode will not properly
switch to Flatbed as intended in the client application (observed in
Simple-Scan).
> NOTE: the initial "READ" command in `sane_start()` is extraneous and
> appears to always return STATUS_BUSY (indeed, the scanner is usually
> busy loading the page). The actual READ is correctly done in
> `dev_acquire()`. It may appear benign in a single page scan mode, however,
> it becomes malfunctioning in the mutipage ADF mode. The initial READ,
> when successful, will cause the skipping of the read block from processing,
> as the actual processing is tied to content read from `dev_acquire()`.
>
> Therefore, the initial READ command in the `sane_start()` is removed.
2024-01-25 22:54:12 +00:00
|
|
|
if (!dev_set_window(dev) ||
|
|
|
|
(dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
|
|
|
|
return dev_stop(dev);
|
|
|
|
}
|
2016-11-06 17:24:02 +00:00
|
|
|
|
|
|
|
if (!dev_cmd_wait(dev, CMD_OBJECT_POSITION))
|
|
|
|
return dev_stop(dev);
|
|
|
|
|
|
|
|
dev->scanning = 1;
|
|
|
|
dev->final_block = 0;
|
|
|
|
dev->blocklen = 0;
|
|
|
|
dev->pixels_per_line = 0;
|
|
|
|
dev->bytes_per_line = 0;
|
|
|
|
dev->ulines = 0;
|
|
|
|
|
|
|
|
set_parameters(dev);
|
|
|
|
|
|
|
|
if (!dev->data && !(dev->data = malloc(DATASIZE)))
|
|
|
|
return ret_cancel(dev, SANE_STATUS_NO_MEM);
|
|
|
|
|
2019-11-05 19:01:53 +00:00
|
|
|
/* this is for jpeg mode only */
|
2016-11-06 17:24:02 +00:00
|
|
|
if (!dev->decData && !(dev->decData = malloc(POST_DATASIZE)))
|
|
|
|
return ret_cancel(dev, SANE_STATUS_NO_MEM);
|
|
|
|
|
|
|
|
if (!dev_acquire(dev))
|
|
|
|
return dev->state;
|
|
|
|
|
|
|
|
/* make sure to have dev->para <= of real size */
|
|
|
|
if (dev->para.pixels_per_line > dev->pixels_per_line) {
|
|
|
|
dev->para.pixels_per_line = dev->pixels_per_line;
|
|
|
|
dev->para.bytes_per_line = dev->pixels_per_line;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->composition == MODE_RGB24)
|
|
|
|
dev->para.bytes_per_line = dev->para.pixels_per_line * 3;
|
|
|
|
else if (dev->composition == MODE_LINEART ||
|
|
|
|
dev->composition == MODE_HALFTONE) {
|
|
|
|
dev->para.bytes_per_line = (dev->para.pixels_per_line + 7) / 8;
|
|
|
|
dev->para.pixels_per_line = dev->para.bytes_per_line * 8;
|
|
|
|
} else {
|
|
|
|
dev->para.bytes_per_line = dev->para.pixels_per_line;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->total_img_size = dev->para.bytes_per_line * dev->para.lines;
|
|
|
|
|
2021-03-30 11:07:24 +00:00
|
|
|
if (isJPEGEnabled(dev) &&
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->composition == MODE_RGB24) {
|
2016-11-06 19:28:03 +00:00
|
|
|
int fd;
|
2016-11-06 17:24:02 +00:00
|
|
|
remove(encTmpFileName);
|
2016-11-06 19:28:03 +00:00
|
|
|
|
|
|
|
/* Precreate temporary file in exclusive mode. */
|
|
|
|
fd = open(encTmpFileName, O_CREAT|O_EXCL, 0600);
|
|
|
|
if (fd == -1) {
|
|
|
|
DBG(3, "%s: %p, can't create temporary file %s: %s\n", __func__,
|
|
|
|
(void *)dev, encTmpFileName, strerror(errno));
|
|
|
|
return ret_cancel(dev, SANE_STATUS_ACCESS_DENIED);
|
|
|
|
}
|
|
|
|
close(fd);
|
2016-11-06 17:24:02 +00:00
|
|
|
}
|
|
|
|
dev->currentDecDataIndex = 0;
|
|
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_Status sane_set_io_mode(SANE_Handle h, SANE_Bool non_blocking)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p, %d\n", __func__, h, non_blocking);
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
if (non_blocking)
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
dev->non_blocking = non_blocking;
|
|
|
|
return SANE_STATUS_GOOD;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
SANE_Status sane_get_select_fd(SANE_Handle h, SANE_Int *fdp)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p, %p\n", __func__, h, (void *)fdp);
|
|
|
|
/* supporting of this will require thread creation */
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
void sane_cancel(SANE_Handle h)
|
2008-12-15 18:31:55 +00:00
|
|
|
{
|
2016-11-06 17:24:02 +00:00
|
|
|
struct device *dev = h;
|
2008-12-15 18:31:55 +00:00
|
|
|
|
2016-11-06 17:24:02 +00:00
|
|
|
DBG(3, "%s: %p\n", __func__, h);
|
|
|
|
dev->cancel = 1;
|
2008-12-15 18:31:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* xerox_mfp.c */
|