Porównaj commity

...

5 Commity

Autor SHA1 Wiadomość Data
Sean Greenslade 0f60b6fdc8 Merge branch 'epson_mods' into 'master'
epsonds: sane_cancel fix and dynamic ADF sizing based on DPI

See merge request sane-project/backends!753
2024-04-16 22:23:59 +00:00
ThierryFR 728ca40272 Merge branch 'escl-force-idle-status' into 'master'
Escl force idle status

See merge request sane-project/backends!835
2024-04-16 20:45:49 +00:00
ThierryFR 113be50f6b Escl force idle status 2024-04-16 20:45:49 +00:00
Sean Greenslade 46bb287b3b epsonds: dynamically size ADF area based on DPI selection
The Epson ES-50 (and friends) supports a larger scanning area when
used at its lowest DPI setting. This adds support for switching to
the larger reported scan area when the DPI setting is at its lowest
value.

The scanner does not directly report the DPI to scan area relationship,
so this code is based on some educated guesses and experimentation.
It's been tested to work on the ES-50 with both scanimage and xsane.
Hopefully it does not interfere with any other scanners using this
backend.
2022-09-03 23:09:36 -07:00
Sean Greenslade 61ab6e9aec epsonds: process cancel requests before next scan begins
Previously, cancel requests would not actually send a cancel packet
to the scanner until after the next scan had started. This would cause
IO errors / scan failures in xsane.
2022-09-03 22:47:31 -07:00
9 zmienionych plików z 261 dodań i 14 usunięć

Wyświetl plik

@ -465,6 +465,7 @@ static SANE_Status info_cb(void *userdata, char *token, int len)
int max = decode_value(value + 4 + 8, 8);
DBG(1, " ADF: max %dx%d @ 100dpi\n", min, max);
eds_set_adf_area_large(s->hw, min, max, 100);
}
}
@ -493,6 +494,7 @@ static SANE_Status info_cb(void *userdata, char *token, int len)
int max = decode_value(value + 4 + 4, 8);
DBG(1, " ADF: max %dx%d @ 100dpi\n", min, max);
eds_set_adf_area_large(s->hw, min, max, 100);
}
}

Wyświetl plik

@ -157,6 +157,25 @@ eds_set_adf_area(struct epsonds_device *dev, int x, int y, int unit)
SANE_UNFIX(dev->adf_y_range.max), unit);
}
void
eds_set_adf_area_large(struct epsonds_device *dev, int x, int y, int unit)
{
dev->adf_x_range_large.min = 0;
dev->adf_x_range_large.max = SANE_FIX(x * MM_PER_INCH / unit);
dev->adf_x_range_large.quant = 0;
dev->adf_y_range_large.min = 0;
dev->adf_y_range_large.max = SANE_FIX(y * MM_PER_INCH / unit);
dev->adf_y_range_large.quant = 0;
DBG(5, "%s: %f,%f %f,%f %d [mm]\n",
__func__,
SANE_UNFIX(dev->adf_x_range_large.min),
SANE_UNFIX(dev->adf_y_range_large.min),
SANE_UNFIX(dev->adf_x_range_large.max),
SANE_UNFIX(dev->adf_y_range_large.max), unit);
}
void
eds_set_tpu_area(struct epsonds_device *dev, int x, int y, int unit)
{

Wyświetl plik

@ -20,6 +20,7 @@ extern SANE_Status eds_add_resolution(epsonds_device *dev, int r);
extern SANE_Status eds_set_resolution_range(epsonds_device *dev, int min, int max);
extern void eds_set_fbf_area(epsonds_device *dev, int x, int y, int unit);
extern void eds_set_adf_area(epsonds_device *dev, int x, int y, int unit);
extern void eds_set_adf_area_large(epsonds_device *dev, int x, int y, int unit);
extern void eds_set_tpu_area(epsonds_device *dev, int x, int y, int unit);
extern SANE_Status eds_add_depth(epsonds_device *dev, SANE_Word depth);

Wyświetl plik

@ -1605,11 +1605,16 @@ device_detect(const char *name, int type, SANE_Status *status)
dev->alignment = dev->fbf_alignment;
} else if (s->hw->has_adf) {
if (dev->adf_x_range_large.max > 0) {
dev->x_range = &dev->adf_x_range_large;
dev->y_range = &dev->adf_y_range_large;
} else {
DBG(17, "ADF AMAX was not sent by scanner, using AREA instead\n");
dev->x_range = &dev->adf_x_range;
dev->y_range = &dev->adf_y_range;
}
dev->x_range = &dev->adf_x_range;
dev->y_range = &dev->adf_y_range;
dev->alignment = dev->adf_alignment;
} else {
DBG(0, "unable to lay on the flatbed or feed the feeder. is that a scanner??\n");
}
@ -2214,9 +2219,21 @@ change_source(epsonds_scanner *s, SANE_Int optindex, char *value)
force_max = SANE_TRUE;
}
SANE_Word min_resolution =
(s->hw->dpi_range.quant) ? s->hw->dpi_range.min : s->hw->res_list[1];
SANE_Bool in_min_resolution = (s->val[OPT_RESOLUTION].w == min_resolution);
if (strcmp(STRING_ADFFRONT, value) == 0 || strcmp(STRING_ADFDUPLEX, value) == 0) {
s->hw->x_range = &s->hw->adf_x_range;
s->hw->y_range = &s->hw->adf_y_range;
if (s->hw->adf_x_range_large.max > 0 && in_min_resolution) {
s->hw->x_range = &s->hw->adf_x_range_large;
s->hw->y_range = &s->hw->adf_y_range_large;
} else {
if(!in_min_resolution) DBG(17, "ADF AMAX was not sent by scanner, using AREA instead\n");
else DBG(17, "Not allowed to use AMAX due to DPI, using AREA instead\n");
s->hw->x_range = &s->hw->adf_x_range;
s->hw->y_range = &s->hw->adf_y_range;
}
s->hw->alignment = s->hw->adf_alignment;
@ -2322,8 +2339,37 @@ setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info)
switch (option) {
case OPT_ADF_SKEW:
case OPT_RESOLUTION:
/* Note: this includes an assumption that only the lowest DPI supports
* the large ADF area. This assumption holds true on at least the ES-50
* and friends, but other scanners need to be tested to see if this is
* universally true or not.
*/
if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFFRONT) == 0 || strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFDUPLEX) == 0) {
SANE_Word min_resolution =
(s->hw->dpi_range.quant) ? s->hw->dpi_range.min : s->hw->res_list[1];
if (*((SANE_Word *) value) == min_resolution) {
DBG(17, "DPI change, setting ADF to large area\n");
s->hw->x_range = &s->hw->adf_x_range_large;
s->hw->y_range = &s->hw->adf_y_range_large;
} else {
DBG(17, "DPI change, setting ADF to normal area\n");
s->hw->x_range = &s->hw->adf_x_range;
s->hw->y_range = &s->hw->adf_y_range;
}
s->opt[OPT_BR_X].constraint.range = s->hw->x_range;
s->opt[OPT_BR_Y].constraint.range = s->hw->y_range;
if (s->val[OPT_BR_X].w > s->hw->x_range->max)
s->val[OPT_BR_X].w = s->hw->x_range->max;
if (s->val[OPT_BR_Y].w > s->hw->y_range->max)
s->val[OPT_BR_Y].w = s->hw->y_range->max;
}
// fall through
case OPT_ADF_SKEW:
case OPT_ADF_CRP:
sval->w = *((SANE_Word *) value);
reload = SANE_TRUE;
@ -2828,6 +2874,13 @@ sane_start(SANE_Handle handle)
char cmd[CMD_BUF_SIZE]; /* take care not to overflow */
SANE_Status status = 0;
/* A pending cancel will interfere with sane_get_parameters, so process it first. */
if (s->canceling)
{
DBG(5, "Cancel request in progress at start of scan.\n");
esci2_can(s);
}
s->pages++;
DBG(5, "** %s, pages = %d, scanning = %d, backside = %d, front fill: %d, back fill: %d\n",
@ -3589,6 +3642,7 @@ sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length, SANE_Int *le
{
esci2_can(s);
*length = 0;
s->canceling = 0;
return SANE_STATUS_CANCELLED;
}
@ -3632,6 +3686,7 @@ sane_cancel(SANE_Handle handle)
{
DBG(1, "** %s\n", __func__);
((epsonds_scanner *)handle)->canceling = SANE_TRUE;
((epsonds_scanner *)handle)->scanning = SANE_FALSE;
}
/*

Wyświetl plik

@ -134,6 +134,8 @@ struct epsonds_device
SANE_Bool has_adf; /* adf */
SANE_Range adf_x_range; /* x range */
SANE_Range adf_y_range; /* y range */
SANE_Range adf_x_range_large; /* x range in largest scan mode (low DPI) */
SANE_Range adf_y_range_large; /* y range in largest scan mode (low DPI) */
SANE_Bool adf_is_duplex; /* supports duplex mode */
SANE_Bool adf_singlepass; /* supports single pass duplex */
SANE_Bool adf_has_skew; /* supports skew correction */

Wyświetl plik

@ -1345,7 +1345,7 @@ sane_cancel(SANE_Handle h)
}
handler->scanner->work = SANE_FALSE;
handler->cancel = SANE_TRUE;
escl_scanner(handler->device, handler->scanner->scanJob, handler->result);
escl_scanner(handler->device, handler->scanner->scanJob, handler->result, SANE_TRUE);
free(handler->result);
handler->result = NULL;
free(handler->scanner->scanJob);
@ -1566,6 +1566,7 @@ sane_start(SANE_Handle h)
handler->decompress_scan_data = SANE_FALSE;
handler->end_read = SANE_FALSE;
if (handler->scanner->work == SANE_FALSE) {
escl_reset_all_jobs(handler->device);
SANE_Status st = escl_status(handler->device,
handler->scanner->source,
NULL,

Wyświetl plik

@ -248,7 +248,11 @@ SANE_Status escl_scan(capabilities_t *scanner,
void escl_scanner(const ESCL_Device *device,
char *scanJob,
char *result);
char *result,
SANE_Bool status);
SANE_Status escl_reset_all_jobs(ESCL_Device *device);
typedef void CURL;

Wyświetl plik

@ -44,7 +44,32 @@ write_callback(void __sane_unused__*str,
* This function is called in the 'sane_cancel' function.
*/
void
escl_scanner(const ESCL_Device *device, char *scanJob, char *result)
escl_delete(const ESCL_Device *device, char *uri)
{
CURL *curl_handle = NULL;
long answer = 0;
if (uri == NULL)
return;
curl_handle = curl_easy_init();
if (curl_handle != NULL) {
escl_curl_url(curl_handle, device, uri);
curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE");
if (curl_easy_perform(curl_handle) == CURLE_OK) {
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &answer);
return;
}
curl_easy_cleanup(curl_handle);
}
}
/**
* \fn void escl_scanner(const ESCL_Device *device, char *result)
* \brief Function that resets the scanner after each scan, using curl.
* This function is called in the 'sane_cancel' function.
*/
void
escl_scanner(const ESCL_Device *device, char *scanJob, char *result, SANE_Bool status)
{
CURL *curl_handle = NULL;
const char *scan_jobs = "/eSCL/";
@ -70,10 +95,15 @@ CURL_CALL:
if (i >= 15) return;
}
curl_easy_cleanup(curl_handle);
if (SANE_STATUS_GOOD != escl_status(device,
PLATEN,
NULL,
NULL))
goto CURL_CALL;
char* end = strrchr(scan_cmd, '/');
*end = 0;
escl_delete(device, scan_cmd);
if (status) {
if (SANE_STATUS_GOOD != escl_status(device,
PLATEN,
NULL,
NULL))
goto CURL_CALL;
}
}
}

Wyświetl plik

@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libxml/parser.h>
@ -270,3 +271,135 @@ clean_data:
}
return (status);
}
static void
print_xml_job_finish(xmlNode *node,
SANE_Status *job)
{
while (node) {
if (node->type == XML_ELEMENT_NODE) {
if (find_nodes_s(node)) {
if (strcmp((const char *)node->name, "JobState") == 0) {
const char *state = (const char *)xmlNodeGetContent(node);
if (!strcmp(state, "Canceled")) {
*job = SANE_STATUS_GOOD;
DBG(10, "jobId Completed SANE_STATUS_GOOD\n");
}
else if (!strcmp(state, "Aborted")) {
*job = SANE_STATUS_GOOD;
DBG(10, "jobId Completed SANE_STATUS_GOOD\n");
}
else if (!strcmp(state, "Completed")) {
*job = SANE_STATUS_GOOD;
DBG(10, "jobId Completed SANE_STATUS_GOOD\n");
}
}
}
}
print_xml_job_finish(node->children, job);
node = node->next;
}
}
static void
print_xml_reset_all_jobs (xmlNode *node,
ESCL_Device *device)
{
DBG(10, "print_xml_reset_all_jobs\n");
SANE_Status status = SANE_STATUS_DEVICE_BUSY;
while (node) {
if (node->type == XML_ELEMENT_NODE) {
if (find_nodes_s(node)) {
if (strcmp((const char *)node->name, "JobUri") == 0) {
DBG(10, "print_xml_reset_all_jobs: %s\n", node->name);
if (device != NULL) {
print_xml_job_finish (node, &status);
if (status == SANE_STATUS_DEVICE_BUSY) {
char *jobUri = (char *)xmlNodeGetContent(node);
char *job = strrchr((const char *)jobUri, '/');
char *scanj = NULL;
if (job != NULL) {
if (strstr(jobUri,"ScanJobs"))
scanj = strdup("ScanJobs");
else
scanj = strdup("ScanJob");
DBG(10, "print_xml_reset_all_jobs: %s/%s\n", scanj, job);
escl_scanner(device, scanj, job, SANE_FALSE);
free(scanj);
}
DBG(10, "print_xml_reset_all_jobs: sleep to finish the job\n");
}
}
}
}
}
print_xml_reset_all_jobs (node->children,
device);
node = node->next;
}
}
/**
* \fn SANE_Status escl_reset_all_jobs (ESCL_Device *device, , char *scanJob)
* \brief Function that forces the end of jobs, using curl.
* This function is called in the 'sane_start' function.
*
* \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
*/
SANE_Status
escl_reset_all_jobs(ESCL_Device *device)
{
CURL *curl_handle = NULL;
xmlDoc *data = NULL;
xmlNode *node = NULL;
struct idle *var = NULL;
const char *scanner_status = "/eSCL/ScannerStatus";
SANE_Status status = SANE_STATUS_DEVICE_BUSY;
DBG(10, "escl_reset_all_jobs\n");
if (device == NULL)
return (SANE_STATUS_NO_MEM);
DBG(10, "1 - escl_reset_all_jobs\n");
var = (struct idle*)calloc(1, sizeof(struct idle));
if (var == NULL)
return (SANE_STATUS_NO_MEM);
DBG(10, "2 - escl_reset_all_jobs\n");
var->memory = malloc(1);
var->size = 0;
curl_handle = curl_easy_init();
escl_curl_url(curl_handle, device, scanner_status);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_s);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L);
CURLcode res = curl_easy_perform(curl_handle);
if (res != CURLE_OK) {
DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res));
status = SANE_STATUS_INVAL;
goto clean_data1;
}
DBG(10, "3 - escl_reset_all_jobs\n");
DBG( 10, "eSCL : Status : %s.\n", var->memory);
data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
if (data == NULL) {
status = SANE_STATUS_NO_MEM;
goto clean_data1;
}
node = xmlDocGetRootElement(data);
if (node == NULL) {
status = SANE_STATUS_NO_MEM;
goto clean1;
}
print_xml_reset_all_jobs (node, device);
status = SANE_STATUS_GOOD;
clean1:
xmlFreeDoc(data);
clean_data1:
xmlCleanupParser();
xmlMemoryDump();
curl_easy_cleanup(curl_handle);
free(var->memory);
free(var);
return status;
}