Porównaj commity

...

8 Commity

Autor SHA1 Wiadomość Data
Nomadbyte 3e0e4cd915 Merge branch 'xerox_mfp-729-read' into 'master'
xerox_mfp: Avoid a buffering stall, when reading image data

Closes #729

See merge request sane-project/backends!827
2024-04-20 11:33:33 +00:00
Ralph Little 0f472aa205 Merge branch 'editorconfig_inline_comment' into 'master'
.editorconfig: inline comments are forbidden

See merge request sane-project/backends!832
2024-04-19 19:53:22 +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
Guillaume Girol a6d63a72ec .editorconfig: inline comments are forbidden
source: https://spec.editorconfig.org/#no-inline-comments

this makes neovim unhappy, at least.
2024-02-26 12:00:00 +00:00
Artur Shepilko a07d498270 xerox_mfp: Avoid a buffering stall, when reading image data
This addresses a buffering stall which could occur, when the length
of free space after the buffered data at the end of the allocated
buffer is less than `USB_BLOCK_SIZE`, i.e.
`(DATAROOM(dev) > 0 && DATAROOM(dev) < USB_BLOCK_SIZE)`.

In such case an expectation for the read request length to be a multiple
of `USB_BLOCK_SIZE`, that is (`DATAROOM(dev) & USB_BLOCK_MASK`), would
result in zero remaining buffer room. Combined with no data consumed out
of the buffer, yet more data to receive for the current block, it would
lead to an infinite loop. This was observed with Samsung SCX-4729FW in
network (TCP) mode via Wi-Fi, with JPEG compression disabled.

The `USB_BLOCK_SIZE` is imposed by USB specs; USB 2.0 requires max
512 byte packet size for bulk transfers with high-speed endpoints.
This is applicable to USB transport. TCP transport does not have such
restriction.

NOTE: `DATAROOM(dev)` returns the length of free space from the end of
the buffered data up till the allocated end of the buffer. That means,
it may not always report the total free space left -- only the length
of the contiguous space. When the free space "wraps around", the
reported free space is short of `dev->dataoff`.
2024-02-13 19:55:32 -06:00
Artur Shepilko fc171e9191 xerox_mfp: Add comments to explain the circular buffer implementation 2024-02-08 12:00:01 -06:00
Artur Shepilko 392aa8cc2e xerox_mfp: Fix whitespace and code formatting 2024-02-08 12:00:01 -06:00
9 zmienionych plików z 407 dodań i 176 usunięć

Wyświetl plik

@ -6,7 +6,8 @@
# Your editor may need a plugin for this configuration to take effect.
# See http://editorconfig.org/#download for details.
root = true ; look no further
; look no further
root = true
[*]
charset = utf-8

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,6 +95,10 @@ CURL_CALL:
if (i >= 15) return;
}
curl_easy_cleanup(curl_handle);
char* end = strrchr(scan_cmd, '/');
*end = 0;
escl_delete(device, scan_cmd);
if (status) {
if (SANE_STATUS_GOOD != escl_status(device,
PLATEN,
NULL,
@ -77,3 +106,4 @@ CURL_CALL:
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;
}

Wyświetl plik

@ -1,6 +1,5 @@
/*
* SANE backend for
* Samsung SCX-4500W
* SANE backend for Samsung SCX-4500W
*
* Network Scanners Support
* Copyright 2010 Alexander Kuznetsov <acca(at)cpan.org>
@ -47,7 +46,8 @@
#define RECV_TIMEOUT 1 /* seconds */
extern int sanei_debug_xerox_mfp;
int tcp_dev_request(struct device *dev,
int
tcp_dev_request(struct device *dev,
SANE_Byte *cmd, size_t cmdlen,
SANE_Byte *resp, size_t *resplen)
{
@ -73,7 +73,9 @@ int tcp_dev_request(struct device *dev,
while (bytes_recv < *resplen && rc > 0) {
rc = recv(dev->dn, resp+bytes_recv, *resplen-bytes_recv, 0);
if (rc > 0) bytes_recv += rc;
if (rc > 0) {
bytes_recv += rc;
}
else {
DBG(1, "%s: error %s, bytes requested: %i, bytes read: %i\n",
__func__, strerror(errno), (int)*resplen, (int)bytes_recv);
@ -93,7 +95,8 @@ int tcp_dev_request(struct device *dev,
return SANE_STATUS_GOOD;
}
SANE_Status tcp_dev_open(struct device *dev)
SANE_Status
tcp_dev_open(struct device *dev)
{
SANE_Status status;
char *strhost;
@ -107,10 +110,13 @@ SANE_Status tcp_dev_open(struct device *dev)
devname = dev->sane.name;
DBG(3, "%s: open %s\n", __func__, devname);
if (strncmp(devname, "tcp", 3) != 0) return SANE_STATUS_INVAL;
if (strncmp(devname, "tcp", 3) != 0)
return SANE_STATUS_INVAL;
devname += 3;
devname = sanei_config_skip_whitespace(devname);
if (!*devname) return SANE_STATUS_INVAL;
if (!*devname)
return SANE_STATUS_INVAL;
devname = sanei_config_get_string(devname, &strhost);
devname = sanei_config_skip_whitespace(devname);
@ -147,7 +153,8 @@ SANE_Status tcp_dev_open(struct device *dev)
void
tcp_dev_close(struct device *dev)
{
if (!dev) return;
if (!dev)
return;
DBG(3, "%s: closing dev %p\n", __func__, (void *)dev);
@ -155,7 +162,8 @@ tcp_dev_close(struct device *dev)
if (dev->scanning) {
dev->cancel = 1;
/* flush READ_IMAGE data */
if (dev->reading) sane_read(dev, NULL, 1, NULL);
if (dev->reading)
sane_read(dev, NULL, 1, NULL);
/* send cancel if not sent before */
if (dev->state != SANE_STATUS_CANCELLED)
ret_cancel(dev, 0);

Wyświetl plik

@ -1414,15 +1414,69 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
dev->blocks++;
}
/* Receive the on-device raw image data into a cyclic aka circular buffer.
*
* The circular buffer implementation is as follows:
* - buffer::{data[DATASIZE], dataoff, datalen};
* - data storage is preallocated to a fixed capacity: DATASIZE;
* - dataoff is used to track the data start (for read);
* - datalen is the stored data's length;
* - DATATAIL() macro is to track the data end position (for write);
* - Both start and end positions may be wrapped around at the DATASIZE,
* (& DATAMASK);
* - DATAROOM() macro is used to determine the currently available
* length of the __contiguous__ storage after the data end (from DATATAIL)
* - write is allowed only into the remaining capacity, overwriting of the
* the buffered but not consumed data is not allowed;
* - once data has been consumed, the dataoff and datalen must be adjusted
* accordingly; dataoff may wrap around (& DATAMASK);
* - when DATAROOM() returns 0, the data must be consumed before any more
* data could be buffered.
*
* NOTE: the DATAROOM() does not wrap around and yields the length
* from DATATAIL() up to the DATASIZE position. So the returned length may
* be shorter than the actual remaining capacity, that is there may be
* more unoccupied space from the beginning of the buffer till dataoff.
*
* This is because the buffer's storage pointer at the write position is passed
* directly to the IO receive call, so it's expected to be contiguous and can't
* wrap around. Thus in such cases this may result in extra IO receive call in
* order to first fill the residual length till DATASIZE, and then continue
* filling from the start of the buffer.
*
* It's all manual and requires some discipline but it does the job.
*/
do {
size_t datalen;
int clrlen; /* cleared lines len */
int olen; /* output len */
/* read as much data into the buffer */
datalen = MIN(dev->blocklen, DATAROOM(dev) & USB_BLOCK_MASK);
/* set up to read as much data as room left in the buffer */
datalen = DATAROOM(dev);
while (datalen && dev->blocklen) {
SANE_Byte *rbuf = dev->data + DATATAIL(dev);
/* USB 2.0 spec requires 512 byte packet max size for bulk transfers
* with high-speed endpoints. Smaller max sizes are allowed
* for slower endpoints (64, 32, 16, 8 for full-speed).
* Some Samsung MFP scanners require requests for at least the
* defined max size, when there are sufficient data available;
* otherwise successfully return 0 bytes.
* For example,
* - requesting 256 bytes with more than 256 bytes data available
* will return 0 bytes;
* - requesting at least 512 will properly fulfill the request;
* - requesting 8 bytes with 16 bytes remaining will return 0 bytes;
* yet requesting 16 bytes (or more) will correctly return the
* remaining 16 bytes.
*/
int usb_datalen = datalen & USB_BLOCK_MASK;
if (usb_datalen)
datalen = usb_datalen;
else if (strcmp(dev->io->ttype, "usb") == 0)
break;
datalen = MIN((size_t)dev->blocklen, datalen);
DBG(9, "<> request len: %zu, [%d, %d; %d]\n",
datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
@ -1431,7 +1485,7 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
SANE_STATUS_GOOD)
return status;
dev->datalen += datalen;
dev->datalen += datalen; /* of actually received data */
dev->blocklen -= datalen;
DBG(9, "<> got %zu, [%d, %d; %d]\n",
@ -1440,7 +1494,7 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
if (dev->blocklen < 0)
return ret_cancel(dev, SANE_STATUS_IO_ERROR);
datalen = MIN(dev->blocklen, DATAROOM(dev) & USB_BLOCK_MASK);
datalen = DATAROOM(dev);
}
if (buf && lenp) { /* read mode */