Marek et al-

I've spent a couple evenings looking at all the various logs that I
have for the C225, and have discovered that it does a different type
of fine calibration from other models. So, I've worked up the attached
patch, against the current sane-backends sources. The idea is to
re-use some of the existing calibration code instead of writing a new
version just for this scanner. I expect this may not work, and if it
does, it will only give us poor quality images. But, it should be
enough to validate that the issue is caused by calibration. You will
need to apply this patch to sane-backends source, and
rebuild/reinstall. I don't have access to any Canon scanners at this
time, so I have only verified that this compiles, unfortunately.

Let me know what you find out, or if you need help.

allan

On Thu, Jul 23, 2020 at 8:53 PM m. allan noah <[email protected]> wrote:
>
> Marek, I'm sorry it has taken me a month to reply, life is a little
> 'different' these days :)
>
> So I have gone back through every email thread I have had about the
> DR-C225, and do have a set of logs from the windows driver. I will
> attempt to determine which bit of magic we are missing. It will take
> me some time, as I am travelling now. If you do not hear from me soon,
> please feel free to reach out. Also, if you want to study up on
> rebuilding sane-backends from source in the meantime, we will need it.
>
> allan
>
> On Fri, Jun 26, 2020 at 4:33 PM Marek <[email protected]> wrote:
> >
> > Dear developers,
> > I wonder if you could help me with resolving a problem with Canon
> > DR-C225 II scanner (canon_dr backend). The output scan is all black. In
> > 2015 there was a thread on the predecessor of this scanner: DR-C225
> > (without 'II'), which suffered from similar problem. Unfortunately that
> > thread seems to be prematurely abandoned without solving the issue.
> > I'm attaching a scanimage log for a page scanned in a grayscale mode (for
> > the color mode the output scan is black as well).
> > Best regards,
> > Marek
>
>
>
> --
> "well, I stand up next to a mountain- and I chop it down with the edge
> of my hand"



-- 
"well, I stand up next to a mountain- and I chop it down with the edge
of my hand"
diff --git a/backend/canon_dr-cmd.h b/backend/canon_dr-cmd.h
index 51f2a06..e77303d 100644
--- a/backend/canon_dr-cmd.h
+++ b/backend/canon_dr-cmd.h
@@ -287,6 +287,17 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes)
 /*counters*/
 /*endorser*/
 
+/*fine calibration*/
+#define set_S_FCAL_datatype(sb, val) sb[0x02] = (unsigned char)val
+/* these are offset, OR with 0x40 to get gain */
+#define S_FCAL_id_f_red                0x00
+#define S_FCAL_id_f_green              0x04
+#define S_FCAL_id_f_blue               0x08
+#define S_FCAL_id_b_red                0x01
+#define S_FCAL_id_b_green              0x05
+#define S_FCAL_id_b_blue               0x09
+
+
 /* ==================================================================== */
 /* OBJECT_POSITION */
 #define OBJECT_POSITION_code    0x31
diff --git a/backend/canon_dr.c b/backend/canon_dr.c
index f6cd5d4..2c796ed 100644
--- a/backend/canon_dr.c
+++ b/backend/canon_dr.c
@@ -390,7 +390,7 @@
 #include "canon_dr.h"
 
 #define DEBUG 1
-#define BUILD 58
+#define BUILD 60
 
 /* values for SANE_DEBUG_CANON_DR env var:
  - errors           5
@@ -1348,7 +1348,8 @@ init_model (struct scanner *s)
     s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG;
     s->duplex_interlace = DUPLEX_INTERLACE_FBfb;
     s->need_ccal = 1;
-    s->need_fcal = 1;
+    s->fcal_src = FCAL_SRC_SCAN;
+    s->fcal_dest = FCAL_DEST_SW;
     /*s->duplex_offset = 432; now set in config file*/
     s->duplex_offset_side = SIDE_BACK;
 
@@ -1372,7 +1373,8 @@ init_model (struct scanner *s)
     s->duplex_interlace = DUPLEX_INTERLACE_2510;
     /*s->duplex_offset = 400; now set in config file*/
     s->need_ccal = 1;
-    s->need_fcal = 1;
+    s->fcal_src = FCAL_SRC_SCAN;
+    s->fcal_dest = FCAL_DEST_SW;
     s->sw_lut = 1;
     /*s->invert_tly = 1;*/
 
@@ -1402,7 +1404,8 @@ init_model (struct scanner *s)
     s->duplex_interlace = DUPLEX_INTERLACE_2510;
     /*s->duplex_offset = 400; now set in config file*/
     s->need_ccal = 1;
-    s->need_fcal = 1;
+    s->fcal_src = FCAL_SRC_SCAN;
+    s->fcal_dest = FCAL_DEST_SW;
     s->sw_lut = 1;
     s->invert_tly = 1;
 
@@ -1427,7 +1430,8 @@ init_model (struct scanner *s)
     s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB;
     s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB;
     s->duplex_interlace = DUPLEX_INTERLACE_FBfb;
-    s->need_fcal_buffer = 1;
+    s->fcal_src = FCAL_SRC_HW;
+    s->fcal_dest = FCAL_DEST_SW;
     s->bg_color = 0x08;
     /*s->duplex_offset = 840; now set in config file*/
     s->sw_lut = 1;
@@ -1604,7 +1635,8 @@ init_model (struct scanner *s)
     s->unknown_byte2 = 0x88;
     s->need_ccal = 1;
     s->ccal_version = 3;
-    s->need_fcal = 1;
+    s->fcal_src = FCAL_SRC_SCAN;
+    s->fcal_dest = FCAL_DEST_SW;
     s->sw_lut = 1;
     s->rgb_format = 1;
     /*s->duplex_offset = 400; now set in config file*/
@@ -1632,7 +1664,8 @@ init_model (struct scanner *s)
     s->unknown_byte2 = 0x88;
     s->need_ccal = 1;
     s->ccal_version = 3;
-    s->need_fcal = 1;
+    s->fcal_src = FCAL_SRC_SCAN;
+    s->fcal_dest = FCAL_DEST_HW;
     s->invert_tly = 1;
     s->rgb_format = 1;
     /*s->duplex_offset = 400; now set in config file*/
@@ -4312,22 +4345,19 @@ sane_start (SANE_Handle handle)
     }
 
     /* AFE cal */
-    if((ret = calibrate_AFE(s))){
+    ret = calibrate_AFE(s);
+    if (ret != SANE_STATUS_GOOD) {
       DBG (5, "sane_start: ERROR: cannot cal afe\n");
       goto errors;
     }
 
     /* fine cal */
-    if((ret = calibrate_fine(s))){
+    ret = calibrate_fine(s);
+    if (ret != SANE_STATUS_GOOD) {
       DBG (5, "sane_start: ERROR: cannot cal fine\n");
       goto errors;
     }
 
-    if((ret = calibrate_fine_buffer(s))){
-      DBG (5, "sane_start: ERROR: cannot cal fine from buffer\n");
-      goto errors;
-    }
-
     /* reset the page counter after calibration */
     s->panel_counter = 0;
     s->prev_page = 0;
@@ -6117,10 +6147,68 @@ calibrate_AFE (struct scanner *s)
   return ret;
 }
 
+/*
+ * fine calibration produces a per-cell offset and gain value,
+ * which is then used to adjust the output from the scanner.
+ * There is quite a bit of variation here, with different models
+ * needing different types/amounts of help from the software.
+ *
+ * This function is a common entry point for all variations.
+ */
+static SANE_Status
+calibrate_fine (struct scanner *s)
+{
+  SANE_Status ret = SANE_STATUS_GOOD;
+
+  DBG (10, "calibrate_fine: start\n");
+
+  if(s->fcal_src == FCAL_SRC_NONE || s->fcal_dest == FCAL_DEST_NONE){
+    DBG (10, "calibrate_fine: not required\n");
+    goto cleanup;
+  }
 
-/* alternative version- extracts data from scanner memory */
+  /* don't recalibrate if we've already done it with these params */
+  if(s->f_res == s->s.dpi_x && s->f_mode == s->s.mode){
+    DBG (10, "calibrate_fine: already done\n");
+    goto cleanup;
+  }
+
+  /* get calibration data from scanner memory */
+  if(s->fcal_src == FCAL_SRC_HW){
+    ret = calibrate_fine_src_hw(s);
+    if (ret != SANE_STATUS_GOOD)
+      goto cleanup;
+  }
+
+  /* get calibration data by making scans */
+  if(s->fcal_src == FCAL_SRC_SCAN){
+    ret = calibrate_fine_src_scan(s);
+    if (ret != SANE_STATUS_GOOD)
+      goto cleanup;
+  }
+
+  /* send calibration data to scanner */
+  if(s->fcal_dest == FCAL_DEST_HW){
+    ret = calibrate_fine_dest_hw(s);
+    if (ret != SANE_STATUS_GOOD)
+      goto cleanup;
+  }
+
+  /* log current cal settings so we won't recalibrate on next scan with same params */
+  s->f_res = s->s.dpi_x;
+  s->f_mode = s->s.mode;
+
+  cleanup:
+
+  DBG (10, "calibrate_fine: finish %d\n",ret);
+
+  return ret;
+}
+
+
+/* extracts fine calibration data from scanner memory */
 static SANE_Status
-calibrate_fine_buffer (struct scanner *s)
+calibrate_fine_src_hw (struct scanner *s)
 {
   SANE_Status ret = SANE_STATUS_GOOD;
   int i, j, k;
@@ -6136,12 +6224,7 @@ calibrate_fine_buffer (struct scanner *s)
   int old_br_y = s->u.br_y;
   int old_source = s->u.source;
 
-  DBG (10, "calibrate_fine_buffer: start\n");
-
-  if(!s->need_fcal_buffer){
-    DBG (10, "calibrate_fine_buffer: not required\n");
-    return ret;
-  }
+  DBG (10, "calibrate_fine_src_hw: start\n");
 
   /* pretend we are doing a 1 line scan in duplex */
   s->u.tl_y = 0;
@@ -6151,19 +6234,14 @@ calibrate_fine_buffer (struct scanner *s)
   /* load our own private copy of scan params */
   ret = update_params(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine_buffer: ERROR: cannot update_params\n");
-    goto cleanup;
-  }
-
-  if(s->f_res == s->s.dpi_x && s->f_mode == s->s.mode){
-    DBG (10, "calibrate_fine_buffer: already done\n");
+    DBG (5, "calibrate_fine_src_hw: ERROR: cannot update_params\n");
     goto cleanup;
   }
 
   /* clean scan params for new scan */
   ret = clean_params(s);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine_buffer: ERROR: cannot clean_params\n");
+    DBG (5, "calibrate_fine_src_hw: ERROR: cannot clean_params\n");
     goto cleanup;
   }
 
@@ -6172,7 +6250,7 @@ calibrate_fine_buffer (struct scanner *s)
 
   in = malloc(reqLen);
   if (!in) {
-    DBG (5, "calibrate_fine_buffer: ERROR: cannot malloc in\n");
+    DBG (5, "calibrate_fine_src_hw: ERROR: cannot malloc in\n");
     ret = SANE_STATUS_NO_MEM;
     goto cleanup;
   }
@@ -6180,11 +6258,11 @@ calibrate_fine_buffer (struct scanner *s)
   /*fine offset*/
   ret = offset_buffers(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine_buffer: ERROR: cannot load offset buffers\n");
+    DBG (5, "calibrate_fine_src_hw: ERROR: cannot load offset buffers\n");
     goto cleanup;
   }
 
-  DBG (5, "calibrate_fine_buffer: %d %x\n", s->s.dpi_x/10, s->s.dpi_x/10);
+  DBG (10, "calibrate_fine_src_hw: %d %x\n", s->s.dpi_x/10, s->s.dpi_x/10);
 
   memset(cmd,0,cmdLen);
   set_SCSI_opcode(cmd, READ_code);
@@ -6194,8 +6272,6 @@ calibrate_fine_buffer (struct scanner *s)
 
   inLen = reqLen;
 
-  hexdump(15, "cmd:", cmd, cmdLen);
-
   ret = do_cmd (
     s, 1, 0,
     cmd, cmdLen,
@@ -6230,14 +6306,12 @@ calibrate_fine_buffer (struct scanner *s)
           s->f_offset[i][j] = 1;
       }
     }
-
-    hexdump(15, "off:", s->f_offset[i], s->s.valid_Bpl);
   }
 
   /*fine gain*/
   ret = gain_buffers(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine_buffer: ERROR: cannot load gain buffers\n");
+    DBG (5, "calibrate_fine_src_hw: ERROR: cannot load gain buffers\n");
     goto cleanup;
   }
 
@@ -6257,8 +6331,6 @@ calibrate_fine_buffer (struct scanner *s)
       set_R_xfer_uid (cmd, codes[k]);
       inLen = reqLen;
 
-      hexdump(15, "cmd:", cmd, cmdLen);
-
       ret = do_cmd (
         s, 1, 0,
         cmd, cmdLen,
@@ -6286,8 +6358,6 @@ calibrate_fine_buffer (struct scanner *s)
     set_R_xfer_uid (cmd, R_FINE_uid_gray);
     inLen = reqLen;
 
-    hexdump(15, "cmd:", cmd, cmdLen);
-
     ret = do_cmd (
       s, 1, 0,
       cmd, cmdLen,
@@ -6308,14 +6378,6 @@ calibrate_fine_buffer (struct scanner *s)
     }
   }
 
-  for(i=0;i<2;i++){
-    hexdump(15, "gain:", s->f_gain[i], s->s.valid_Bpl);
-  }
-
-  /* log current cal type */
-  s->f_res = s->s.dpi_x;
-  s->f_mode = s->s.mode;
-
   cleanup:
 
   if(in){
@@ -6327,16 +6389,16 @@ calibrate_fine_buffer (struct scanner *s)
   s->u.br_y = old_br_y;
   s->u.source = old_source;
 
-  DBG (10, "calibrate_fine_buffer: finish %d\n",ret);
+  DBG (10, "calibrate_fine_src_hw: finish %d\n",ret);
 
   return ret;
 }
 
 /*
- * makes several scans, adjusts fine calibration
+ * makes several scans, generates fine calibration data
  */
 static SANE_Status
-calibrate_fine (struct scanner *s)
+calibrate_fine_src_scan (struct scanner *s)
 {
   SANE_Status ret = SANE_STATUS_GOOD;
   int i, j, k;
@@ -6348,12 +6410,7 @@ calibrate_fine (struct scanner *s)
   int old_br_y = s->u.br_y;
   int old_source = s->u.source;
 
-  DBG (10, "calibrate_fine: start\n");
-
-  if(!s->need_fcal){
-    DBG (10, "calibrate_fine: not required\n");
-    return ret;
-  }
+  DBG (10, "calibrate_fine_src_scan: start\n");
 
   /* always cal with a short scan in duplex */
   s->u.tl_y = 0;
@@ -6363,12 +6420,7 @@ calibrate_fine (struct scanner *s)
   /* load our own private copy of scan params */
   ret = update_params(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot update_params\n");
-    goto cleanup;
-  }
-
-  if(s->f_res == s->s.dpi_x && s->f_mode == s->s.mode){
-    DBG (10, "calibrate_fine: already done\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot update_params\n");
     goto cleanup;
   }
 
@@ -6382,7 +6434,7 @@ calibrate_fine (struct scanner *s)
   /* make buffers to hold the images */
   ret = image_buffers(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot load buffers\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot load buffers\n");
     goto cleanup;
   }
 
@@ -6393,28 +6445,28 @@ calibrate_fine (struct scanner *s)
   /* need to tell it we want duplex */
   ret = ssm_buffer(s);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot ssm buffer\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot ssm buffer\n");
     goto cleanup;
   }
 
   /* set window command */
   ret = set_window(s);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot set window\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot set window\n");
     goto cleanup;
   }
 
-  /*handle fifth pass (fine offset), lamp off*/
-  DBG (15, "calibrate_fine: offset\n");
+  /* first pass (fine offset), lamp off */
+  DBG (15, "calibrate_fine_src_scan: offset\n");
   ret = calibration_scan(s,0xff);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot make offset cal scan\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot make offset cal scan\n");
     goto cleanup;
   }
 
   ret = offset_buffers(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot load offset buffers\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot load offset buffers\n");
     goto cleanup;
   }
 
@@ -6429,17 +6481,17 @@ calibrate_fine (struct scanner *s)
     hexdump(15, "off:", s->f_offset[i], s->s.valid_Bpl);
   }
 
-  /*handle sixth pass (fine gain), lamp on*/
-  DBG (15, "calibrate_fine: gain\n");
+  /* second pass (fine gain), lamp on */
+  DBG (15, "calibrate_fine_src_scan: gain\n");
   ret = calibration_scan(s,0xfe);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot make gain cal scan\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot make gain cal scan\n");
     goto cleanup;
   }
 
   ret = gain_buffers(s,1);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "calibrate_fine: ERROR: cannot load gain buffers\n");
+    DBG (5, "calibrate_fine_src_scan: ERROR: cannot load gain buffers\n");
     goto cleanup;
   }
 
@@ -6457,10 +6509,6 @@ calibrate_fine (struct scanner *s)
     hexdump(15, "gain:", s->f_gain[i], s->s.valid_Bpl);
   }
 
-  /* log current cal type */
-  s->f_res = s->s.dpi_x;
-  s->f_mode = s->s.mode;
-
   cleanup:
 
   /* recover user settings */
@@ -6468,13 +6516,105 @@ calibrate_fine (struct scanner *s)
   s->u.br_y = old_br_y;
   s->u.source = old_source;
 
-  DBG (10, "calibrate_fine: finish %d\n",ret);
+  DBG (10, "calibrate_fine_src_scan: finish %d\n",ret);
+
+  return ret;
+}
+
+/* write calibration data to scanner memory and delete from struct */
+static SANE_Status
+calibrate_fine_dest_hw (struct scanner *s)
+{
+  SANE_Status ret = SANE_STATUS_GOOD;
+  int i, j, k;
+
+  unsigned char cmd[SEND_len];
+  size_t cmdLen = SEND_len;
+
+  unsigned char * out = NULL;
+  size_t outLen = 0;
+
+  DBG (10, "calibrate_fine_dest_hw: start\n");
+
+  /* calibration buffers in scanner are single color channel, but 16 bit, plus 4 byte header */
+  outLen = s->s.width*2 + 4;
+
+  out = malloc(outLen);
+  if (!out) {
+    DBG (5, "calibrate_fine_dest_hw: ERROR: cannot malloc out\n");
+    ret = SANE_STATUS_NO_MEM;
+    goto cleanup;
+  }
+
+  // sides
+  for(i=0;i<2;i++){
+
+    // colors
+    for(j=0;j<3;j++){
+
+      int codes[] = {
+        S_FCAL_id_f_red, S_FCAL_id_f_green, S_FCAL_id_f_blue,
+        S_FCAL_id_b_red, S_FCAL_id_b_green, S_FCAL_id_b_blue};
+
+      // offset
+      memset(cmd,0,cmdLen);
+      set_SCSI_opcode(cmd, SEND_code);
+      set_S_xfer_datatype (cmd, SR_datatype_fineoffset);
+      set_S_xfer_length (cmd, outLen);
+
+      set_S_FCAL_datatype (out, codes[i*3+j]);
+
+      for(k=0; k<s->s.valid_width; k++){
+        out[4+k*2] = 0;
+        out[4+k*2+1] = s->f_offset[i][k*3+j];
+      }
+
+      ret = do_cmd (
+        s, 1, 0,
+        cmd, cmdLen,
+        out, outLen,
+        NULL, 0
+      );
+      if (ret != SANE_STATUS_GOOD)
+        goto cleanup;
+
+      // gain
+      set_S_FCAL_datatype (out, codes[i*3+j] | 0x40);
+
+      for(k=0; k<s->s.valid_width; k++){
+        out[4+k*2] = 0;
+        out[4+k*2+1] = s->f_gain[i][k*3+j];
+      }
+
+      ret = do_cmd (
+        s, 1, 0,
+        cmd, cmdLen,
+        out, outLen,
+        NULL, 0
+      );
+      if (ret != SANE_STATUS_GOOD)
+        goto cleanup;
+
+    }
+  }
+
+  cleanup:
+
+  /*blast the fine cal data we generated above, so reading code wont apply it*/
+  offset_buffers(s,0);
+  gain_buffers(s,0);
+
+  if(out){
+    free(out);
+  }
+
+  DBG (10, "calibrate_fine_dest_hw: finish %d\n",ret);
 
   return ret;
 }
 
 /*
- * sends AFE params, and ingests entire duplex image into buffers
+ * does a simple scan, ingests entire duplex image into buffers
  */
 static SANE_Status
 calibration_scan (struct scanner *s, int scan)
diff --git a/backend/canon_dr.h b/backend/canon_dr.h
index 4ffb360..0fd6ae4 100644
--- a/backend/canon_dr.h
+++ b/backend/canon_dr.h
@@ -167,11 +167,21 @@ struct scanner
   int max_y_fb;
 
   int can_color;     /* actually might be in vpd, but which bit? */
+
   int need_ccal;     /* scanner needs software to help with afe calibration */
-  int need_fcal;     /* scanner needs software to help with fine calibration */
-  int need_fcal_buffer; /* software to apply calibration stored in scanner*/
   int ccal_version;  /* 0 in most scanners, 3 in newer ones */
 
+  int fcal_src;      /* where fine offset/gain calibration data comes from */
+  int fcal_dest;     /* where fine offset/gain calibration data is used */
+
+#define FCAL_SRC_NONE 0  /* scanner does not require fine calibration */
+#define FCAL_SRC_SCAN 1  /* make calibration scans, store gain/offset in struct */
+#define FCAL_SRC_HW 2    /* calibration permanently stored in scanner, downloaded into struct */
+
+#define FCAL_DEST_NONE 0 /* scanner does not require fine calibration */
+#define FCAL_DEST_SW 1   /* use gain/offset in struct to adjust output in software */
+#define FCAL_DEST_HW 2   /* send calibration data into scanner for use in hardware */
+
   int has_counter;
   int has_rif;
   int has_adf;
@@ -609,7 +619,9 @@ static SANE_Status gain_buffers (struct scanner *s, int setup);
 
 static SANE_Status calibrate_AFE(struct scanner *s);
 static SANE_Status calibrate_fine(struct scanner *s);
-static SANE_Status calibrate_fine_buffer(struct scanner *s);
+static SANE_Status calibrate_fine_src_scan(struct scanner *s);
+static SANE_Status calibrate_fine_src_hw(struct scanner *s);
+static SANE_Status calibrate_fine_dest_hw(struct scanner *s);
 
 static SANE_Status write_AFE (struct scanner *s);
 static SANE_Status calibration_scan (struct scanner *s, int);

Reply via email to