Hello,

I think it's the first time I'm trying to post patches to sane-devel,
so please bear with me.

I'm a long time user of EPSON 3490 Perfection scanner. It basically
works fine with out-of-the-box SANE packaged by Fedora, but there are
issues with firmware downloading, which I'm finally trying to
eliminate (I used to download firmware with separate utility).

The scanner is using the snapscan backend, it's a Hi-Speed USB 2.0
device based on the ALi M5623 scanner chip. The scanner is using some
sort of SCSI over USB protocol. The chip contains a clone of the old
8-bit Intel 8032 microcontroller (maybe 8052), using an external 2 MB
of SDRAM (plus motor and CCD circuitry etc).

I don't have access to M5623 datasheet (anyone?), but from the 2-page
brief I understand the chip contains a flash or similar memory
(writable at least once) which is used to initialize USB interface,
present itself as some sort of non-functional scanner (with early
version of firmware), and download and run the real firmware.

Because there are 3 patch files, I'm attaching them (as inline text).
Please let me know if it's not appropriate.
The patches are tested with SANE v. 1.4.0.

1. The debugging output (SANE_DEBUG_SNAPSCAN=255) may be a bit
misleading. I'm also fixing debug formatting issues:

 [snapscan] usb_cmd: cmdlen=6, datalen=0
-[snapscan] usb_write: writing:  0x12 0x00 0x00 0x00 0x24 0x00
+[snapscan] usb_write: writing: 0x12 0x00 0x00 0x00 0x24 0x00
 [snapscan] Written 6 bytes
-[snapscan] usb_read: reading:  0xf9 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-[snapscan] Read 8 bytes
+[snapscan] usb_read: received 8 bytes: 0xf9 0x00 0x00 0x00 0x00 0x00 0x00 0x00

The bogus 0xf8 ... is no more:

-[snapscan] usb_read Only 0 bytes read
-[snapscan] usb_read: reading:  0xf8 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-[snapscan] Read 0 bytes
+[snapscan] usb_read: received 0 bytes
+[snapscan] usb_read: only 0 bytes read <<<<< this is an error

There are no changes to any communication over USB etc. - purely
cosmetics.

Snapscan: better USB debugging

This patch mostly removes the bogus debug data on read when no/short data was received.
Fixes minor formatting issues as well.

 [snapscan] usb_cmd: cmdlen=6, datalen=0
-[snapscan] usb_write: writing:  0x12 0x00 0x00 0x00 0x24 0x00
+[snapscan] usb_write: writing: 0x12 0x00 0x00 0x00 0x24 0x00
 [snapscan] Written 6 bytes
-[snapscan] usb_read: reading:  0xf9 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-[snapscan] Read 8 bytes
+[snapscan] usb_read: received 8 bytes: 0xf9 0x00 0x00 0x00 0x00 0x00 0x00 0x00

it's mostly this: the bogus 0xf8 ... is no more:

-[snapscan] usb_read Only 0 bytes read
-[snapscan] usb_read: reading:  0xf8 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-[snapscan] Read 0 bytes
+[snapscan] usb_read: received 0 bytes
+[snapscan] usb_read: only 0 bytes read <<< this is an error so I left it in


diff --git a/backend/snapscan-usb.c b/backend/snapscan-usb.c
index 6e6751f0f..4329e96b2 100644
--- a/backend/snapscan-usb.c
+++ b/backend/snapscan-usb.c
@@ -278,15 +278,15 @@ static SANE_Status usb_write(int fd, const void *buf, size_t n) {
     size_t bytes_written = n;
 
     static const char me[] = "usb_write";
-    DBG(DL_DATA_TRACE, "%s: writing: %s\n",me,usb_debug_data(dbgmsg,buf,n));
+    DBG(DL_DATA_TRACE, "%s: writing:%s\n", me, usb_debug_data(dbgmsg, buf, n));
 
     status = sanei_usb_write_bulk(fd, (const SANE_Byte*)buf, &bytes_written);
-    if(bytes_written != n) {
-      DBG (DL_MAJOR_ERROR, "%s Only %lu bytes written\n",me, (u_long) bytes_written);
-        status = SANE_STATUS_IO_ERROR;
-    }
     urb_counters->write_urbs += (bytes_written + 7) / 8;
     DBG (DL_DATA_TRACE, "Written %lu bytes\n", (u_long) bytes_written);
+    if (bytes_written != n) {
+        DBG(DL_MAJOR_ERROR, "%s: only %lu bytes written\n", me, (u_long)bytes_written);
+        status = SANE_STATUS_IO_ERROR;
+    }
     return status;
 }
 
@@ -297,13 +297,15 @@ static SANE_Status usb_read(SANE_Int fd, void *buf, size_t n) {
     size_t bytes_read = n;
 
     status = sanei_usb_read_bulk(fd, (SANE_Byte*)buf, &bytes_read);
+    urb_counters->read_urbs += ((bytes_read + 63) / 64);
+    if (bytes_read)
+        DBG(DL_DATA_TRACE, "%s: received %lu bytes:%s\n", me, (u_long)bytes_read, usb_debug_data(dbgmsg, buf, bytes_read));
+    else
+        DBG(DL_DATA_TRACE, "%s: received 0 bytes\n", me);
     if (bytes_read != n) {
-        DBG (DL_MAJOR_ERROR, "%s Only %lu bytes read\n",me, (u_long) bytes_read);
+        DBG(DL_MAJOR_ERROR, "%s: only %lu bytes read\n",me, (u_long)bytes_read);
         status = SANE_STATUS_IO_ERROR;
     }
-    urb_counters->read_urbs += ((63 + bytes_read) / 64);
-    DBG(DL_DATA_TRACE, "%s: reading: %s\n",me,usb_debug_data(dbgmsg,buf,n));
-    DBG(DL_DATA_TRACE, "Read %lu bytes\n", (u_long) bytes_read);
     return status;
 }
 
2. Simplifying usb_read_status(). The "scsistatus" is never used, the
"transaction_status" is always used - no need for NULL checking.
This patch is needed for #3.

Snapscan: simplify usb_read_status()

The "scsistatus" is never used, the "transaction_status" is always
used - no need for NULL checking.

diff --git a/backend/snapscan-usb.c b/backend/snapscan-usb.c
index 4329e96b2..6b67bbc0d 100644
--- a/backend/snapscan-usb.c
+++ b/backend/snapscan-usb.c
@@ -309,8 +309,7 @@ static SANE_Status usb_read(SANE_Int fd, void *buf, size_t n) {
     return status;
 }
 
-static SANE_Status usb_read_status(int fd, int *scsistatus, int *transaction_status,
-                                   char command)
+static SANE_Status usb_read_status(int fd, unsigned char *transaction_status, char command)
 {
     static const char me[] = "usb_read_status";
     unsigned char status_buf[8];
@@ -319,14 +318,9 @@ static SANE_Status usb_read_status(int fd, int *scsistatus, int *transaction_sta
 
     RETURN_ON_FAILURE(usb_read(fd,status_buf,8));
 
-    if(transaction_status)
-        *transaction_status = status_buf[0];
-
+    *transaction_status = status_buf[0];
     scsistat = (status_buf[1] & STATUS_MASK) >> 1;
 
-    if(scsistatus)
-        *scsistatus = scsistat;
-
     switch(scsistat) {
     case GOOD:
         return SANE_STATUS_GOOD;
@@ -357,7 +351,8 @@ static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
                     void *dst, size_t * dst_size)
 {
   static const char me[] = "usb_cmd";
-  int status,tstatus;
+  SANE_Status status;
+  unsigned char tstatus;
   int cmdlen,datalen;
   char command;
 
@@ -380,7 +375,7 @@ static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
   RETURN_ON_FAILURE( usb_write(fd,src,cmdlen) );
 
   /* Read status */
-  RETURN_ON_FAILURE( usb_read_status(fd, NULL, &tstatus, command) );
+  RETURN_ON_FAILURE(usb_read_status(fd, &tstatus, command));
 
   /* Send data only if the scanner is expecting it */
   if(datalen > 0 && (tstatus == TRANSACTION_WRITE)) {
@@ -388,7 +383,7 @@ static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
       RETURN_ON_FAILURE( usb_write(fd, ((const SANE_Byte *) src) + cmdlen, datalen) );
 
       /* Read status */
-      RETURN_ON_FAILURE( usb_read_status(fd, NULL, &tstatus, command) );
+      RETURN_ON_FAILURE(usb_read_status(fd, &tstatus, command));
   }
 
   /* Receive data only when new data is waiting */
@@ -396,7 +391,7 @@ static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
       RETURN_ON_FAILURE( usb_read(fd,dst,*dst_size) );
 
       /* Read status */
-      RETURN_ON_FAILURE( usb_read_status(fd, NULL, &tstatus, command) );
+      RETURN_ON_FAILURE(usb_read_status(fd, &tstatus, command));
   }
 
   if(tstatus != TRANSACTION_COMPLETED) {
3. This patch fixes the firmware loading and running problem.
It appears that there is some timing issue in the scanner boot
firmware, which causes the final SCSI command (0x2A = SEND, data type
code = 0x87 = vendor specific) to not get the response (apparently
the scanner jumps to the new firmware without waiting for the response
to be collected over USB by the host). Sometimes (very rarely) the
response gets through.

 [snapscan] Downloading /lib/firmware/Esfw52.bin
 [snapscan] Size of firmware: 63239
 [snapscan] snapscan_cmd
 [snapscan] snapscani_usb_cmd(0,0x1a7a1500,63249,0x0,0x0 (0))
 [snapscan] atomic_usb_cmd(0,0x1a7a1500,63249,0x0,0x0 (0))
 [snapscan] usb_cmd(0,0x1a7a1500,63249,0x0,0x0 (0))
 [snapscan] usb_cmd: cmdlen=10, datalen=63239
 [snapscan] usb_write: writing: 0x2a 0x00 0x87 0x00 0x00 0x00 0x00 0xf7 0x07 
0x00
 [snapscan] Written 10 bytes
 [snapscan] usb_read: received 8 bytes: 0xf8 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 [snapscan] usb_write: writing: 0x02 0xf2 0xd5 0x32 0xff 0xff 0xff 0xff 0xff 
0xff ...
 [snapscan] Written 63239 bytes
-[60 seconds]
+[2 seconds]
 [snapscan] usb_read: received 0 bytes
 [snapscan] usb_read: only 0 bytes read
-[snapscan] sane_snapscan_open: download_firmware command failed: Error during 
device I/O
-scanimage: open of device snapscan:libusb:001:043 failed: Error during device 
I/O
+[snapscan] wait_scanner_ready
+[snapscan] test_unit_ready
+[snapscan] snapscan_cmd

These 3 patches don't fix all problems I can see (e.g. error handling),
but the scanner now downloads firmware reliably.

Snapscan: Fix firmware upload on EPSON Perfection 3490

EPSON Perfection 3490 Photo scanner is based on ALi M5623 chip.
When executing SEND (FIRMWARE) command, its built-in ROM doesn't
allow enough time to acknowledge the transfer, before jumping to
the download code.
>From time to time, the response is sent, thoough.

The same problem may be present on other scanners.

Handle this situation gracefully.

diff --git a/backend/snapscan-usb.c b/backend/snapscan-usb.c
index 6b67bbc0d..a89be65f5 100644
--- a/backend/snapscan-usb.c
+++ b/backend/snapscan-usb.c
@@ -81,6 +81,9 @@
 #define SHM_W 0
 #endif
 
+#define DEFAULT_TIMEOUT (30 * 1000) // 30 seconds
+#define FW_DOWNLOAD_TIMEOUT (2 * 1000) // 2 seconds
+
 /* Global variables */
 
 static snapscan_mutex_t snapscan_mutex;
@@ -371,6 +374,8 @@ static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
 
   DBG(DL_DATA_TRACE, "%s: cmdlen=%d, datalen=%d\n",me,cmdlen,datalen);
 
+  sanei_usb_set_timeout(DEFAULT_TIMEOUT);
+
   /* Send command to scanner */
   RETURN_ON_FAILURE( usb_write(fd,src,cmdlen) );
 
@@ -379,11 +384,19 @@ static SANE_Status usb_cmd(int fd, const void *src, size_t src_size,
 
   /* Send data only if the scanner is expecting it */
   if(datalen > 0 && (tstatus == TRANSACTION_WRITE)) {
+      int fw_download = command == SEND && ((unsigned char *)src)[2] == 0x87;
       /* Send data to scanner */
       RETURN_ON_FAILURE( usb_write(fd, ((const SANE_Byte *) src) + cmdlen, datalen) );
 
-      /* Read status */
-      RETURN_ON_FAILURE(usb_read_status(fd, &tstatus, command));
+      /* If downloading firmware, don't wait too long, scanner may not send a response */
+      if (fw_download)
+          sanei_usb_set_timeout(FW_DOWNLOAD_TIMEOUT);
+      status = usb_read_status(fd, &tstatus, command);
+      if (fw_download) {
+          sanei_usb_set_timeout(DEFAULT_TIMEOUT);
+          if (status == SANE_STATUS_IO_ERROR)
+              return SANE_STATUS_GOOD;
+      }
   }
 
   /* Receive data only when new data is waiting */
-- 
Krzysztof "Chris" Hałasa

Reply via email to