diff --git a/ChangeLog b/ChangeLog index 5d59e32c6..725ed9535 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-10-04 Nicolas Martin + * backend/pixma.c, backend/pixma.h, backend/pixma_common.c, + backend/pixma_io_sanei.c, backend/pixma_mp150.c, + doc/sane-pixma.man, doc/description/pixma.desc: + MP970 scanning improvements, up to 4800 dpi. On the way soon, + network BJNP protocol designed by Louis Lagendijk to be added to CVS. + MX7600 reported to work fine with the backend. + ADF scanning: + - improved for latest PIXMAs like MX850, MX310. + - bug fix in Sane_start, when scanning several pages with ADF. + ADF DUPLEX scanning: + - new code for ADF Duplex, (to be tested) based on a MX850 Snoop. Changes + might fit also MP830 (To be confirmed). + TPU scanning: + - MP970 TPU scanning: Protocol works, get scanned TPU images with 48 bits + to 24 bits conversion, full 48 bit version yet to be debugged. + 2008-10-03 m. allan noah * backend/epjitsu.[ch]: backend v17: - increase scan height ~1/2 inch due to head offset diff --git a/backend/pixma.c b/backend/pixma.c index 63be25c5f..dc9075f80 100644 --- a/backend/pixma.c +++ b/backend/pixma.c @@ -174,11 +174,11 @@ cleanup_device_list (void) { int i; for (i = 0; dev_list[i]; i++) - { - free (CONST_CAST (void *, dev_list[i]->name)); - free (CONST_CAST (void *, dev_list[i]->model)); - free (CONST_CAST (void *, dev_list[i])); - } + { + free (CONST_CAST (void *, dev_list[i]->name)); + free (CONST_CAST (void *, dev_list[i]->model)); + free (CONST_CAST (void *, dev_list[i])); + } } free (dev_list); dev_list = NULL; @@ -201,16 +201,16 @@ find_scanners (void) SANE_Device *sdev = (SANE_Device *) calloc (1, sizeof (*sdev)); char *name, *model; if (!sdev) - goto nomem; + goto nomem; name = strdup (pixma_get_device_id (i)); model = strdup (pixma_get_device_model (i)); if (!name || !model) - { - free (name); - free (model); - free (sdev); - goto nomem; - } + { + free (name); + free (model); + free (sdev); + goto nomem; + } sdev->name = name; sdev->model = model; sdev->vendor = vendor_str; @@ -281,23 +281,23 @@ clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info) { SANE_Word value = va[i]; if (value < range->min) - { - value = range->min; - } + { + value = range->min; + } else if (value > range->max) - { - value = range->max; - } + { + value = range->max; + } if (range->quant != 0) - { - value = (value - range->min + range->quant / 2) / - range->quant * range->quant; - } + { + value = (value - range->min + range->quant / 2) / + range->quant * range->quant; + } if (value != va[i]) - { - va[i] = value; - *info |= SANE_INFO_INEXACT; - } + { + va[i] = value; + *info |= SANE_INFO_INEXACT; + } } } @@ -317,21 +317,21 @@ select_value_from_list (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Word mindelta = abs (value - list[1]); SANE_Word nearest = list[1]; for (j = 2; j <= list[0]; j++) - { - SANE_Word delta = abs (value - list[j]); - if (delta < mindelta) - { - mindelta = delta; - nearest = list[j]; - } - if (mindelta == 0) - break; - } + { + SANE_Word delta = abs (value - list[j]); + if (delta < mindelta) + { + mindelta = delta; + nearest = list[j]; + } + if (mindelta == 0) + break; + } if (va[i] != nearest) - { - va[i] = nearest; - *info |= SANE_INFO_INEXACT; - } + { + va[i] = nearest; + *info |= SANE_INFO_INEXACT; + } } } @@ -346,51 +346,51 @@ control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, { case SANE_ACTION_GET_VALUE: switch (opt->sod.type) - { - case SANE_TYPE_BOOL: - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - *(SANE_Word *) v = opt->val.w; - break; - default: - return SANE_STATUS_UNSUPPORTED; - } + { + case SANE_TYPE_BOOL: + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + *(SANE_Word *) v = opt->val.w; + break; + default: + return SANE_STATUS_UNSUPPORTED; + } return SANE_STATUS_GOOD; case SANE_ACTION_SET_VALUE: switch (opt->sod.type) - { - case SANE_TYPE_BOOL: - val = *(SANE_Word *) v; - if (val != SANE_TRUE && val != SANE_FALSE) - return SANE_STATUS_INVAL; - opt->val.w = val; - break; - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE) - clamp_value (ss, n, v, info); - else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST) - select_value_from_list (ss, n, v, info); - opt->val.w = *(SANE_Word *) v; - break; - default: - return SANE_STATUS_UNSUPPORTED; - } + { + case SANE_TYPE_BOOL: + val = *(SANE_Word *) v; + if (val != SANE_TRUE && val != SANE_FALSE) + return SANE_STATUS_INVAL; + opt->val.w = val; + break; + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE) + clamp_value (ss, n, v, info); + else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST) + select_value_from_list (ss, n, v, info); + opt->val.w = *(SANE_Word *) v; + break; + default: + return SANE_STATUS_UNSUPPORTED; + } *info |= opt->info; return SANE_STATUS_GOOD; case SANE_ACTION_SET_AUTO: switch (opt->sod.type) - { - case SANE_TYPE_BOOL: - case SANE_TYPE_INT: - case SANE_TYPE_FIXED: - opt->val.w = opt->def.w; - break; - default: - return SANE_STATUS_UNSUPPORTED; - } + { + case SANE_TYPE_BOOL: + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + opt->val.w = opt->def.w; + break; + default: + return SANE_STATUS_UNSUPPORTED; + } *info |= opt->info; return SANE_STATUS_GOOD; } @@ -409,45 +409,45 @@ control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE) { switch (a) - { - case SANE_ACTION_GET_VALUE: - strcpy (str, opt->val.s); - break; - case SANE_ACTION_SET_AUTO: - str = opt->def.s; - /* fall through */ - case SANE_ACTION_SET_VALUE: - strncpy (opt->val.s, str, opt->sod.size - 1); - *info |= opt->info; - break; - } + { + case SANE_ACTION_GET_VALUE: + strcpy (str, opt->val.s); + break; + case SANE_ACTION_SET_AUTO: + str = opt->def.s; + /* fall through */ + case SANE_ACTION_SET_VALUE: + strncpy (opt->val.s, str, opt->sod.size - 1); + *info |= opt->info; + break; + } return SANE_STATUS_GOOD; } else { switch (a) - { - case SANE_ACTION_GET_VALUE: - strcpy (str, slist[opt->val.w]); - break; - case SANE_ACTION_SET_AUTO: - str = opt->def.ptr; - /* fall through */ - case SANE_ACTION_SET_VALUE: - i = 0; - while (slist[i] && strcasecmp (str, slist[i]) != 0) - i++; - if (!slist[i]) - return SANE_STATUS_INVAL; - if (strcmp (slist[i], str) != 0) - { - strcpy (str, slist[i]); - *info |= SANE_INFO_INEXACT; - } - opt->val.w = i; - *info |= opt->info; - break; - } + { + case SANE_ACTION_GET_VALUE: + strcpy (str, slist[opt->val.w]); + break; + case SANE_ACTION_SET_AUTO: + str = opt->def.ptr; + /* fall through */ + case SANE_ACTION_SET_VALUE: + i = 0; + while (slist[i] && strcasecmp (str, slist[i]) != 0) + i++; + if (!slist[i]) + return SANE_STATUS_INVAL; + if (strcmp (slist[i], str) != 0) + { + strcpy (str, slist[i]); + *info |= SANE_INFO_INEXACT; + } + opt->val.w = i; + *info |= opt->info; + break; + } return SANE_STATUS_GOOD; } } @@ -461,37 +461,37 @@ control_option (pixma_sane_t * ss, SANE_Int n, result = SANE_STATUS_UNSUPPORTED; switch (n) { - case opt_gamma_table: - switch (a) - { - case SANE_ACTION_SET_VALUE: - clamp_value (ss, n, v, info); - for (i = 0; i != 4096; i++) - ss->gamma_table[i] = *((SANE_Int *) v + i); - break; - case SANE_ACTION_GET_VALUE: - for (i = 0; i != 4096; i++) - *((SANE_Int *) v + i) = ss->gamma_table[i]; - break; - case SANE_ACTION_SET_AUTO: - pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, - sizeof (ss->gamma_table)); - break; - default: - return SANE_STATUS_UNSUPPORTED; - } - return SANE_STATUS_GOOD; + case opt_gamma_table: + switch (a) + { + case SANE_ACTION_SET_VALUE: + clamp_value (ss, n, v, info); + for (i = 0; i != 4096; i++) + ss->gamma_table[i] = *((SANE_Int *) v + i); + break; + case SANE_ACTION_GET_VALUE: + for (i = 0; i != 4096; i++) + *((SANE_Int *) v + i) = ss->gamma_table[i]; + break; + case SANE_ACTION_SET_AUTO: + pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, + sizeof (ss->gamma_table)); + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + return SANE_STATUS_GOOD; case opt_button_update: if (a == SANE_ACTION_SET_VALUE) - { - update_button_state (ss, info); - return SANE_STATUS_GOOD; - } + { + update_button_state (ss, info); + return SANE_STATUS_GOOD; + } else - { - return SANE_STATUS_INVAL; - } + { + return SANE_STATUS_INVAL; + } break; } @@ -518,10 +518,10 @@ control_option (pixma_sane_t * ss, SANE_Int n, { case opt_custom_gamma: if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO) - { - if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b)) - *info |= SANE_INFO_RELOAD_OPTIONS; - } + { + if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b)) + *info |= SANE_INFO_RELOAD_OPTIONS; + } break; } @@ -582,6 +582,7 @@ calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp) sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL; sp->source = ss->source_map[OVAL (opt_source).w]; + sp->adf_pageid = ss->page_count; error = pixma_check_scan_param (ss->s, sp); if (error < 0) @@ -646,7 +647,7 @@ init_option_descriptors (pixma_sane_t * ss) ss->source_map[i] = PIXMA_SOURCE_ADFDUP; i++; } -#if 0 +#if 1 if (cfg->cap & PIXMA_CAP_TPU) { ss->source_list[i] = SANE_I18N ("Transparency Unit"); @@ -728,36 +729,36 @@ reader_loop (pixma_sane_t * ss) "To cancel, press 'GRAY' button.\n"); #endif while (pixma_wait_event (ss->s, 10) != 0) - { - } + { + } while (!start) - { - uint32_t events; - if (ss->reader_stop) - { - count = PIXMA_ECANCELED; - goto done; - } - events = pixma_wait_event (ss->s, 1000); - switch (events & ~PIXMA_EV_ACTION_MASK) - { - case PIXMA_EV_BUTTON1: - start = 1; - break; - case PIXMA_EV_BUTTON2: - count = PIXMA_ECANCELED; - goto done; - } - } + { + uint32_t events; + if (ss->reader_stop) + { + count = PIXMA_ECANCELED; + goto done; + } + events = pixma_wait_event (ss->s, 1000); + switch (events & ~PIXMA_EV_ACTION_MASK) + { + case PIXMA_EV_BUTTON1: + start = 1; + break; + case PIXMA_EV_BUTTON2: + count = PIXMA_ECANCELED; + goto done; + } + } } count = pixma_scan (ss->s, &ss->sp); if (count >= 0) { while ((count = pixma_read_image (ss->s, buf, bufsize)) > 0) - { - if (write_all (ss, buf, count) != count) - pixma_cancel (ss->s); - } + { + if (write_all (ss, buf, count) != count) + pixma_cancel (ss->s); + } } done: @@ -839,7 +840,7 @@ terminate_reader_task (pixma_sane_t * ss, int *exit_code) if (result == pid) { if (exit_code) - *exit_code = status; + *exit_code = status; return pid; } else @@ -886,10 +887,10 @@ start_reader_task (pixma_sane_t * ss) { pid = sanei_thread_begin (reader_process, ss); if (pid > 0) - { - close (ss->wpipe); - ss->wpipe = -1; - } + { + close (ss->wpipe); + ss->wpipe = -1; + } } else { @@ -923,8 +924,8 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen) do { if (ss->cancel) - /* ss->rpipe has already been closed by sane_cancel(). */ - return SANE_STATUS_CANCELLED; + /* ss->rpipe has already been closed by sane_cancel(). */ + return SANE_STATUS_CANCELLED; count = read (ss->rpipe, buf, size); } while (count == -1 && errno == EINTR); @@ -932,12 +933,12 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen) if (count == -1) { if (errno == EAGAIN) - return SANE_STATUS_GOOD; + return SANE_STATUS_GOOD; if (!ss->cancel) - { - PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n", - strerror (errno))); - } + { + PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n", + strerror (errno))); + } close (ss->rpipe); ss->rpipe = -1; terminate_reader_task (ss, NULL); @@ -964,16 +965,16 @@ read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen) close (ss->rpipe); ss->rpipe = -1; if (terminate_reader_task (ss, &status) != -1 - && status != SANE_STATUS_GOOD) - { - return status; - } + && status != SANE_STATUS_GOOD) + { + return status; + } else - { - /* either terminate_reader_task failed or - rpipe was closed but we expect more data */ - return SANE_STATUS_IO_ERROR; - } + { + /* either terminate_reader_task failed or + rpipe was closed but we expect more data */ + return SANE_STATUS_IO_ERROR; + } } if (readlen) *readlen = count; @@ -1051,17 +1052,17 @@ sane_open (SANE_String_Const name, SANE_Handle * h) for (ss = first_scanner; ss; ss = ss->next) { if (strcmp (pixma_get_string (ss->s, PIXMA_STRING_ID), name) == 0) - { - /* We have already opened it! */ - return SANE_STATUS_DEVICE_BUSY; - } + { + /* We have already opened it! */ + return SANE_STATUS_DEVICE_BUSY; + } } i = 0; while (strcmp (pixma_get_device_id (i), name) != 0) { if (++i >= nscanners) - return SANE_STATUS_INVAL; + return SANE_STATUS_INVAL; } cfg = pixma_get_device_config (i); if ((cfg->cap & PIXMA_CAP_EXPERIMENT) != 0) @@ -1070,13 +1071,13 @@ sane_open (SANE_String_Const name, SANE_Handle * h) pixma_dbg (1, "WARNING:" "Experimental backend CAN DAMAGE your hardware!\n"); if (getenv_atoi ("PIXMA_EXPERIMENT", 0) == 0) - { - pixma_dbg (1, "Experimental SANE backend for %s is disabled " - "by default.\n", pixma_get_device_model (i)); - pixma_dbg (1, "To enable it, set the environment variable " - "PIXMA_EXPERIMENT to non-zero.\n"); - return SANE_STATUS_UNSUPPORTED; - } + { + pixma_dbg (1, "Experimental SANE backend for %s is disabled " + "by default.\n", pixma_get_device_model (i)); + pixma_dbg (1, "To enable it, set the environment variable " + "PIXMA_EXPERIMENT to non-zero.\n"); + return SANE_STATUS_UNSUPPORTED; + } #else return SANE_STATUS_UNSUPPORTED; #endif @@ -1220,9 +1221,20 @@ sane_start (SANE_Handle h) if (!ss) return SANE_STATUS_INVAL; if (!ss->idle && ss->scanning) - return SANE_STATUS_INVAL; + { + PDBG (pixma_dbg (3, "Warning in Sane_start: !idle && scanning. idle=%d, ss->scanning=%d\n", + ss->idle, ss->scanning)); + if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP) + return SANE_STATUS_INVAL; + } ss->cancel = SANE_FALSE; + if (ss->idle || + ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED || + ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU) + ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */ + else + ss->page_count++; if (calc_scan_param (ss, &ss->sp) < 0) return SANE_STATUS_INVAL; ss->image_bytes_read = 0; @@ -1233,12 +1245,6 @@ sane_start (SANE_Handle h) { ss->output_line_size = ss->sp.w * ss->sp.channels * (ss->sp.depth / 8); ss->byte_pos_in_line = 0; - if (ss->idle || - ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED || - ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU) - ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */ - else - ss->page_count++; ss->last_read_status = SANE_STATUS_GOOD; ss->scanning = SANE_TRUE; ss->idle = SANE_FALSE; @@ -1277,38 +1283,36 @@ sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) the end of line, we've to remove it here in the backend! */ sum = 0; while (sum < maxlen) - { - if (ss->byte_pos_in_line < ss->output_line_size) - { - n = ss->output_line_size - ss->byte_pos_in_line; - if ((maxlen - sum) < n) - n = maxlen - sum; - status = read_image (ss, buf, n, &n); - if (n == 0) - break; - sum += n; - buf += n; - ss->byte_pos_in_line += n; - } - else - { - /* skip padding */ - n = ss->sp.line_size - ss->byte_pos_in_line; - if (n > (int) sizeof (temp)) - { - PDBG (pixma_dbg (3, - "Inefficient skip buffer. Should be %d\n", - n)); - n = sizeof (temp); - } - status = read_image (ss, temp, n, &n); - if (n == 0) - break; - ss->byte_pos_in_line += n; - if (ss->byte_pos_in_line == ss->sp.line_size) - ss->byte_pos_in_line = 0; - } - } + { + if (ss->byte_pos_in_line < ss->output_line_size) + { + n = ss->output_line_size - ss->byte_pos_in_line; + if ((maxlen - sum) < n) + n = maxlen - sum; + status = read_image (ss, buf, n, &n); + if (n == 0) + break; + sum += n; + buf += n; + ss->byte_pos_in_line += n; + } + else + { + /* skip padding */ + n = ss->sp.line_size - ss->byte_pos_in_line; + if (n > (int) sizeof (temp)) + { + PDBG (pixma_dbg (3, "Inefficient skip buffer. Should be %d\n", n)); + n = sizeof (temp); + } + status = read_image (ss, temp, n, &n); + if (n == 0) + break; + ss->byte_pos_in_line += n; + if (ss->byte_pos_in_line == ss->sp.line_size) + ss->byte_pos_in_line = 0; + } + } } if (ss->cancel) status = SANE_STATUS_CANCELLED; diff --git a/backend/pixma.h b/backend/pixma.h index 0aea9f22b..de92bc9b1 100644 --- a/backend/pixma.h +++ b/backend/pixma.h @@ -266,6 +266,9 @@ struct pixma_scan_param_t /** \see #pixma_paper_source_t */ pixma_paper_source_t source; + + /** The current page # in the same ADF scan session, 0 in non ADF */ + unsigned adf_pageid; }; /** PIXMA model information */ diff --git a/backend/pixma_common.c b/backend/pixma_common.c index fd5cbb8cf..ef8d65b30 100644 --- a/backend/pixma_common.c +++ b/backend/pixma_common.c @@ -122,16 +122,16 @@ pixma_hexdump (int level, const void *d_, unsigned len) line[9] = ':'; p = line + 10; for (c = 0; c != 16 && (ofs + c) < len; c++) - { - u8tohex (d[ofs + c], p); - p[2] = ' '; - p += 3; - if (c == 7) - { - p[0] = ' '; - p++; - } - } + { + u8tohex (d[ofs + c], p); + p[2] = ' '; + p += 3; + if (c == 7) + { + p[0] = ' '; + p++; + } + } p[0] = '\0'; pixma_dbg (level, "%s\n", line); ofs += c; @@ -621,67 +621,65 @@ pixma_read_image (pixma_t * s, void *buf, unsigned len) if (s->underrun) { if (s->cur_image_size < s->param->image_size) - { - ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff); - } - else - { - PDBG (pixma_dbg - (3, "pixma_read_image():completed (underrun detected)\n")); - s->scanning = 0; - } + { + ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff); + } + else + { + PDBG (pixma_dbg + (3, "pixma_read_image():completed (underrun detected)\n")); + s->scanning = 0; + } return ib.wptr - (uint8_t *) buf; } while (ib.wptr != ib.wend) { if (ib.rptr == ib.rend) - { - ib.rptr = ib.rend = NULL; - result = s->ops->fill_buffer (s, &ib); - if (result < 0) - goto cancel; - if (result == 0) - { /* end of image? */ - s->ops->finish_scan (s); -#ifndef NDEBUG - if (s->cur_image_size != s->param->image_size) - { - pixma_dbg (1, "WARNING:image size mismatches\n"); - pixma_dbg (1, - " %u expected (%d lines) but %u received (%d lines)\n", - s->param->image_size, s->param->h, - s->cur_image_size, - s->cur_image_size / s->param->line_size); - if ((s->cur_image_size % s->param->line_size) != 0) - { - pixma_dbg (1, - "BUG:received data not multiple of line_size\n"); - } - } -#endif /* !NDEBUG */ - if (s->cur_image_size < s->param->image_size) - { - s->underrun = 1; - ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff); - } - else - { - PDBG (pixma_dbg (3, "pixma_read_image():completed\n")); - s->scanning = 0; - } - break; - } - s->cur_image_size += result; - PASSERT (s->cur_image_size <= s->param->image_size); - } + { + ib.rptr = ib.rend = NULL; + result = s->ops->fill_buffer (s, &ib); + if (result < 0) + goto cancel; + if (result == 0) + { /* end of image? */ + s->ops->finish_scan (s); + if (s->cur_image_size != s->param->image_size) + { + pixma_dbg (1, "WARNING:image size mismatches\n"); + pixma_dbg (1, + " %u expected (%d lines) but %u received (%d lines)\n", + s->param->image_size, s->param->h, + s->cur_image_size, + s->cur_image_size / s->param->line_size); + if ((s->cur_image_size % s->param->line_size) != 0) + { + pixma_dbg (1, + "BUG:received data not multiple of line_size\n"); + } + } + if (s->cur_image_size < s->param->image_size) + { + s->underrun = 1; + ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff); + } + else + { + PDBG (pixma_dbg (3, "pixma_read_image():completed\n")); + s->scanning = 0; + } + break; + } + s->cur_image_size += result; + PASSERT (s->cur_image_size <= s->param->image_size); + } if (ib.rptr) - { - unsigned count = MIN (ib.rend - ib.rptr, ib.wend - ib.wptr); - memcpy (ib.wptr, ib.rptr, count); - ib.rptr += count; - ib.wptr += count; - } + { + unsigned count = MIN (ib.rend - ib.rptr, ib.wend - ib.wptr); + memcpy (ib.wptr, ib.rptr, count); + ib.rptr += count; + ib.wptr += count; + } } s->imagebuf = ib; /* store rptr and rend */ return ib.wptr - (uint8_t *) buf; @@ -764,37 +762,37 @@ pixma_check_scan_param (pixma_t * s, pixma_scan_param_t * sp) case PIXMA_SOURCE_TPU: if ((s->cfg->cap & PIXMA_CAP_TPU) != PIXMA_CAP_TPU) - { - sp->source = PIXMA_SOURCE_FLATBED; - PDBG (pixma_dbg - (1, "WARNING: TPU unsupported, fallback to flatbed.\n")); - } + { + sp->source = PIXMA_SOURCE_FLATBED; + PDBG (pixma_dbg + (1, "WARNING: TPU unsupported, fallback to flatbed.\n")); + } break; case PIXMA_SOURCE_ADF: if ((s->cfg->cap & PIXMA_CAP_ADF) != PIXMA_CAP_ADF) - { - sp->source = PIXMA_SOURCE_FLATBED; - PDBG (pixma_dbg - (1, "WARNING: ADF unsupported, fallback to flatbed.\n")); - } + { + sp->source = PIXMA_SOURCE_FLATBED; + PDBG (pixma_dbg + (1, "WARNING: ADF unsupported, fallback to flatbed.\n")); + } break; case PIXMA_SOURCE_ADFDUP: if ((s->cfg->cap & PIXMA_CAP_ADFDUP) != PIXMA_CAP_ADFDUP) - { - if (s->cfg->cap & PIXMA_CAP_ADF) - { - sp->source = PIXMA_SOURCE_ADF; - } - else - { - sp->source = PIXMA_SOURCE_FLATBED; - } - PDBG (pixma_dbg - (1, "WARNING: ADF duplex unsupported, fallback to %d.\n", - sp->source)); - } + { + if (s->cfg->cap & PIXMA_CAP_ADF) + { + sp->source = PIXMA_SOURCE_ADF; + } + else + { + sp->source = PIXMA_SOURCE_FLATBED; + } + PDBG (pixma_dbg + (1, "WARNING: ADF duplex unsupported, fallback to %d.\n", + sp->source)); + } break; } diff --git a/backend/pixma_io_sanei.c b/backend/pixma_io_sanei.c index 531f1287a..ee9f61e02 100644 --- a/backend/pixma_io_sanei.c +++ b/backend/pixma_io_sanei.c @@ -272,19 +272,19 @@ pixma_collect_devices (const struct pixma_config_t *const pixma_devices[]) for (i = 0; pixma_devices[i]; i++) { for (cfg = pixma_devices[i]; cfg->name; cfg++) - { - sanei_usb_find_devices (cfg->vid, cfg->pid, attach); - si = first_scanner; - while (j < nscanners) - { - PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n", - cfg->name, si->devname)); - si->cfg = cfg; - read_serial_number (si); - si = si->next; - j++; - } - } + { + sanei_usb_find_devices (cfg->vid, cfg->pid, attach); + si = first_scanner; + while (j < nscanners) + { + PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n", + cfg->name, si->devname)); + si->cfg = cfg; + read_serial_number (si); + si = si->next; + j++; + } + } } return nscanners; } diff --git a/backend/pixma_mp150.c b/backend/pixma_mp150.c index 3be832dd6..ecac9c1cf 100644 --- a/backend/pixma_mp150.c +++ b/backend/pixma_mp150.c @@ -57,6 +57,19 @@ #include "pixma_common.h" #include "pixma_io.h" +/* Some macro code to enhance readability */ +#define RET_IF_ERR(x) do { \ + if ((error = (x)) < 0) \ + return error; \ + } while(0) + +#define WAIT_INTERRUPT(x) do { \ + error = handle_interrupt (s, x); \ + if (s->cancel) \ + return PIXMA_ECANCELED; \ + if (error != PIXMA_ECANCELED && error < 0) \ + return error; \ + } while(0) #ifdef __GNUC__ # define UNUSED(v) (void) v @@ -136,6 +149,8 @@ enum mp150_cmd_t cmd_scan_param_3 = 0xd820, cmd_scan_start_3 = 0xd920, cmd_status_3 = 0xda20, + cmd_get_tpu_info_3 = 0xf520, + cmd_set_tpu_info_3 = 0xea20, cmd_e920 = 0xe920 /* seen in MP800 */ }; @@ -153,10 +168,12 @@ typedef struct mp150_t uint8_t *data_left_ofs; unsigned data_left_len; int shift[3]; - unsigned lines_shift; + unsigned color_shift; + unsigned stripe_shift; + uint8_t tpu_datalen; + uint8_t tpu_data[0x40]; } mp150_t; - /* STAT: 0x0606 = ok, 0x1515 = failed (PIXMA_ECANCELED), @@ -212,11 +229,58 @@ typedef struct mp150_t static void mp150_finish_scan (pixma_t * s); +static int +is_scanning_from_adf (pixma_t * s) +{ + return (s->param->source == PIXMA_SOURCE_ADF + || s->param->source == PIXMA_SOURCE_ADFDUP); +} + +static int +is_scanning_from_adfdup (pixma_t * s) +{ + return (s->param->source == PIXMA_SOURCE_ADFDUP); +} + +static int +is_scanning_from_tpu (pixma_t * s) +{ + return (s->param->source == PIXMA_SOURCE_TPU); +} + +static void +new_cmd_tpu_msg (pixma_t *s, pixma_cmdbuf_t * cb, uint16_t cmd) +{ + pixma_newcmd (cb, cmd, 0, 0); + cb->buf[3] = (is_scanning_from_tpu (s)) ? 0x01 : 0x00; +} + +static int +start_session (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + + new_cmd_tpu_msg (s, &mp->cb, cmd_start_session); + return pixma_exec (s, &mp->cb); +} + static int start_scan_3 (pixma_t * s) { mp150_t *mp = (mp150_t *) s->subdriver; - return pixma_exec_short_cmd (s, &mp->cb, cmd_scan_start_3); + + new_cmd_tpu_msg (s, &mp->cb, cmd_scan_start_3); + return pixma_exec (s, &mp->cb); +} + +static int +send_cmd_start_calibrate_ccd_3 (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + + pixma_newcmd (&mp->cb, cmd_start_calibrate_ccd_3, 0, 0); + mp->cb.buf[3] = 0x01; + return pixma_exec (s, &mp->cb); } static int @@ -241,7 +305,11 @@ static int has_paper (pixma_t * s) { mp150_t *mp = (mp150_t *) s->subdriver; - return (mp->current_status[1] == 0); + + if (is_scanning_from_adfdup (s)) + return (mp->current_status[1] == 0 || mp->current_status[2] == 0); + else + return (mp->current_status[1] == 0); } static void @@ -265,20 +333,6 @@ send_cmd_e920 (pixma_t * s) return pixma_exec_short_cmd (s, &mp->cb, cmd_e920); } -static int -send_cmd_start_calibrate_ccd_3 (pixma_t * s) -{ - mp150_t *mp = (mp150_t *) s->subdriver; - return pixma_exec_short_cmd (s, &mp->cb, cmd_start_calibrate_ccd_3); -} - -static int -start_session (pixma_t * s) -{ - mp150_t *mp = (mp150_t *) s->subdriver; - return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session); -} - static int select_source (pixma_t * s) { @@ -289,42 +343,68 @@ select_source (pixma_t * s) if (s->cfg->pid == MP830_PID) { switch (s->param->source) - { - case PIXMA_SOURCE_ADF: - data[0] = 2; - data[5] = 1; - data[6] = 1; - break; + { + case PIXMA_SOURCE_ADF: + data[0] = 2; + data[5] = 1; + data[6] = 1; + break; - case PIXMA_SOURCE_ADFDUP: - data[0] = 2; - data[5] = 3; - data[6] = 3; - break; + case PIXMA_SOURCE_ADFDUP: + data[0] = 2; + data[5] = 3; + data[6] = 3; + break; - case PIXMA_SOURCE_TPU: - PDBG (pixma_dbg (1, "BUG:select_source(): unsupported source %d\n", - s->param->source)); - /* fall through */ + case PIXMA_SOURCE_TPU: + PDBG (pixma_dbg (1, "BUG:select_source(): unsupported source %d\n", + s->param->source)); + /* fall through */ - case PIXMA_SOURCE_FLATBED: - data[0] = 1; - data[1] = 1; - break; - } + case PIXMA_SOURCE_FLATBED: + data[0] = 1; + data[1] = 1; + break; + } } else { data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1; data[1] = 1; if (mp->generation == 2) - { - data[5] = 1; - } + { + data[5] = 1; + } } return pixma_exec (s, &mp->cb); } +static int +send_get_tpu_info_3 (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mp->cb, cmd_get_tpu_info_3, 0, 0x34); + RET_IF_ERR (pixma_exec (s, &mp->cb)); + memcpy (mp->tpu_data, data, 0x34); + return error; +} + +static int +send_set_tpu_info_3 (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + + if (mp->tpu_datalen == 0) + return 0; + data = pixma_newcmd (&mp->cb, cmd_set_tpu_info_3, 0x34, 0); + memcpy (data, mp->tpu_data, 0x34); + return pixma_exec (s, &mp->cb); +} + static int send_gamma_table (pixma_t * s) { @@ -338,9 +418,9 @@ send_gamma_table (pixma_t * s) data[0] = (s->param->channels == 3) ? 0x10 : 0x01; pixma_set_be16 (0x1004, data + 2); if (lut) - memcpy (data + 4, lut, 4096); + memcpy (data + 4, lut, 4096); else - pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096); + pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096); } else { @@ -349,26 +429,26 @@ send_gamma_table (pixma_t * s) data[0] = 0x10; pixma_set_be16 (0x0804, data + 2); if (lut) - { - int i; - for (i = 0; i < 1024; i++) - { - int j = (i << 2) + (i >> 8); - data[4 + 2 * i + 0] = lut[j]; - data[4 + 2 * i + 1] = lut[j]; - } - } + { + int i; + for (i = 0; i < 1024; i++) + { + int j = (i << 2) + (i >> 8); + data[4 + 2 * i + 0] = lut[j]; + data[4 + 2 * i + 1] = lut[j]; + } + } else - { - int i; - pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 2048); - for (i = 0; i < 1024; i++) - { - int j = (i << 1) + (i >> 9); - data[4 + 2 * i + 0] = data[4 + j]; - data[4 + 2 * i + 1] = data[4 + j]; - } - } + { + int i; + pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 2048); + for (i = 0; i < 1024; i++) + { + int j = (i << 1) + (i >> 9); + data[4 + 2 * i + 0] = data[4 + j]; + data[4 + 2 * i + 1] = data[4 + j]; + } + } } return pixma_exec (s, &mp->cb); } @@ -414,36 +494,35 @@ get_cis_ccd_line_size (pixma_t * s) return (s->param->line_size * ((is_ccd_grayscale (s)) ? 3 : 1)); } -static int -is_scanning_from_adf (pixma_t * s) -{ - return (s->param->source == PIXMA_SOURCE_ADF - || s->param->source == PIXMA_SOURCE_ADFDUP); -} - static unsigned calc_shifting (pixma_t * s) { mp150_t *mp = (mp150_t *) s->subdriver; unsigned base_shift; + /* Default: no shift to apply (e.g. CIS sensord) */ + mp->stripe_shift = 0; + mp->color_shift = 0; + /* If color plane shift (CCD devices), how many pixels shift */ switch (s->cfg->pid) { + case MP970_PID: /* MP970 at 4800 dpi */ + if (s->param->xdpi == 4800) + mp->stripe_shift = 3; + /* fall through */ case MP800_PID: case MP810_PID: case MP830_PID: case MP960_PID: - case MP970_PID: - mp->lines_shift = 0; if (s->param->ydpi > 75) - mp->lines_shift = s->param->ydpi / 50; + mp->color_shift = s->param->ydpi / 50; break; default: /* all CIS devices */ - mp->lines_shift = 0; + break; } - base_shift = get_cis_ccd_line_size (s) * mp->lines_shift; + base_shift = get_cis_ccd_line_size (s) * mp->color_shift; /* If color plane shift, how to apply the shift */ switch (s->cfg->pid) @@ -463,7 +542,7 @@ calc_shifting (pixma_t * s) mp->shift[1] = base_shift; mp->shift[2] = 0; } - return mp->lines_shift; + return (2 * mp->color_shift + mp->stripe_shift); } static int @@ -472,7 +551,7 @@ send_scan_param (pixma_t * s) mp150_t *mp = (mp150_t *) s->subdriver; uint8_t *data; unsigned raw_width = calc_raw_width (mp, s->param); - unsigned h = MIN (s->param->h + 2 * calc_shifting (s), + unsigned h = MIN (s->param->h + calc_shifting (s), s->cfg->height * s->param->ydpi / 75); if (mp->generation <= 2) @@ -494,9 +573,20 @@ send_scan_param (pixma_t * s) else { data = pixma_newcmd (&mp->cb, cmd_scan_param_3, 0x38, 0); - data[0x00] = (s->param->source == PIXMA_SOURCE_ADF) ? 0x02 : 0x01; + data[0x00] = (is_scanning_from_adf (s)) ? 0x02 : 0x01; data[0x01] = 0x01; + if (is_scanning_from_tpu (s)) + { + data[0x00] = 0x04; + data[0x01] = 0x02; + data[0x1e] = 0x02; + } data[0x02] = 0x01; + if (is_scanning_from_adfdup (s)) + { + data[0x02] = 0x03; + data[0x03] = 0x03; + } data[0x05] = 0x01; /* This one also seen at 0. Don't know yet what's used for */ pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x08); pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x0a); @@ -505,7 +595,9 @@ send_scan_param (pixma_t * s) pixma_set_be32 (raw_width, data + 0x14); pixma_set_be32 (h, data + 0x18); data[0x1c] = ((s->param->channels != 1) || is_ccd_grayscale (s)) ? 0x08 : 0x04; - data[0x1d] = s->param->depth * ((is_ccd_grayscale (s)) ? 3 : s->param->channels); /* bits per pixel */ + data[0x1d] = s->param->depth * + ((is_ccd_grayscale (s)) ? 3 : s->param->channels) * + ((is_scanning_from_tpu (s)) ? 2 : 1); /* bits per pixel */ data[0x1f] = 0x01; data[0x20] = 0xff; data[0x21] = 0x81; @@ -525,32 +617,8 @@ query_status_3 (pixma_t * s) status_len = 8; data = pixma_newcmd (&mp->cb, cmd_status_3, 0, status_len); - error = pixma_exec (s, &mp->cb); - if (error >= 0) - { - memcpy (mp->current_status, data, status_len); - } - return error; -} - -static int -init_ccd_3 (pixma_t * s) -{ - mp150_t *mp = (mp150_t *) s->subdriver; - uint8_t *data; - int error, status_len; - - status_len = 8; - error = send_cmd_start_calibrate_ccd_3 (s); - if (error >= 0) - { - data = pixma_newcmd (&mp->cb, cmd_end_calibrate_ccd_3, 0, status_len); - error = pixma_exec (s, &mp->cb); - if (error >= 0) - { - memcpy (mp->current_status, data, status_len); - } - } + RET_IF_ERR (pixma_exec (s, &mp->cb)); + memcpy (mp->current_status, data, status_len); return error; } @@ -563,13 +631,10 @@ query_status (pixma_t * s) status_len = (mp->generation == 1) ? 12 : 16; data = pixma_newcmd (&mp->cb, cmd_status, 0, status_len); - error = pixma_exec (s, &mp->cb); - if (error >= 0) - { - memcpy (mp->current_status, data, status_len); - PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u busy=%u\n", + RET_IF_ERR (pixma_exec (s, &mp->cb)); + memcpy (mp->current_status, data, status_len); + PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u busy=%u\n", data[1], data[8], data[7], data[9])); - } return error; } @@ -623,19 +688,16 @@ read_image_block (pixma_t * s, uint8_t * header, uint8_t * data) memcpy (data, mp->cb.buf + hlen, datalen); data += datalen; if (mp->cb.reslen == 512) - { - error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen); - if (error < 0) - return error; - datalen += error; - } + { + error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen); + RET_IF_ERR (error); + datalen += error; + } } mp->state = state_scanning; mp->cb.expected_reslen = 0; - error = pixma_check_result (&mp->cb); - if (error < 0) - return error; + RET_IF_ERR (pixma_check_result (&mp->cb)); if (mp->cb.reslen < hlen) return PIXMA_EPROTO; return datalen; @@ -650,11 +712,10 @@ read_error_info (pixma_t * s, void *buf, unsigned size) int error; data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len); - error = pixma_exec (s, &mp->cb); - if (error >= 0 && buf) + RET_IF_ERR (pixma_exec (s, &mp->cb)); + if (buf && len < size) { - if (len < size) - size = len; + size = len; /* NOTE: I've absolutely no idea what the returned data mean. */ memcpy (buf, data, size); error = len; @@ -704,24 +765,45 @@ handle_interrupt (pixma_t * s, int timeout) return 1; } +static int +init_ccd_lamp_3 (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + int error, status_len, tmo; + + status_len = 8; + RET_IF_ERR (query_status (s)); + RET_IF_ERR (query_status (s)); + RET_IF_ERR (send_cmd_start_calibrate_ccd_3 (s)); + RET_IF_ERR (query_status (s)); + tmo = 20; /* like Windows driver, CCD lamp adjustment */ + while (--tmo >= 0) + { + data = pixma_newcmd (&mp->cb, cmd_end_calibrate_ccd_3, 0, status_len); + RET_IF_ERR (pixma_exec (s, &mp->cb)); + memcpy (mp->current_status, data, status_len); + PDBG (pixma_dbg (3, "Lamp status: %u , timeout in: %u\n", data[0], tmo)); + if (mp->current_status[0] == 3 || !is_scanning_from_tpu (s)) + break; + WAIT_INTERRUPT (1000); + } + return error; +} + static int wait_until_ready (pixma_t * s) { mp150_t *mp = (mp150_t *) s->subdriver; int error, tmo = 60; - error = (mp->generation == 3) ? query_status_3 (s) : query_status (s); - if (error < 0) - return error; + RET_IF_ERR ((mp->generation == 3) ? query_status_3 (s) + : query_status (s)); while (!is_calibrated (s)) { - error = handle_interrupt (s, 1000); if (mp->generation == 3) - error = query_status_3 (s); - if (s->cancel) - return PIXMA_ECANCELED; - if (error != PIXMA_ECANCELED && error < 0) - return error; + RET_IF_ERR (query_status_3 (s)); + WAIT_INTERRUPT (1000); if (--tmo == 0) { PDBG (pixma_dbg (1, "WARNING:Timed out in wait_until_ready()\n")); @@ -731,14 +813,146 @@ wait_until_ready (pixma_t * s) #if 0 /* If we use sanei_usb_*, we sometimes lose interrupts! So poll the * status here. */ - error = query_status (s); - if (error < 0) - return error; + RET_IF_ERR (query_status (s)); #endif } return 0; } +static uint8_t * +shift_colors (uint8_t * dptr, uint8_t * sptr, + unsigned w, unsigned dpi, unsigned pid, + int * colshft, unsigned strshft) +{ + unsigned i, sr, sg, sb, st; + sr = colshft[0]; sg = colshft[1]; sb = colshft[2]; + + for (i = 0; i < w; i++) + { + /* MP970 at 4800 dpi exception stripes shift */ + st = (pid == MP970_PID && dpi == 4800 && (i % 2) == 0) ? strshft : 0; + + *sptr++ = *(dptr++ + sr + st); + *sptr++ = *(dptr++ + sg + st); + *sptr++ = *(dptr++ + sb + st); + } + return dptr; +} + +static uint8_t * +rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w) +{ + unsigned i, g; + + for (i = 0; i < w; i++) + { + g = *sptr++; + g += *sptr++; + *gptr++ = (*sptr++ + g) / 3; + } + return gptr; +} + +static void +reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, unsigned n, + unsigned m, unsigned w, unsigned line_size) +{ + unsigned i; + + for (i = 0; i < w; i++) + { + memcpy (linebuf + c * (n * (i % m) + i / m), sptr + c * i, c); + } + memcpy (sptr, linebuf, line_size); +} + +static void +mp970_reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, + unsigned w, unsigned line_size) +{ + unsigned i, i8; + + for (i = 0; i < w; i++) + { + i8 = i % 8; + memcpy (linebuf + c * (i + i8 - ((i8 > 3) ? 7 : 0)), sptr + c * i, c); + } + memcpy (sptr, linebuf, line_size); +} + +static unsigned +pack_48_24_bpc (uint8_t * sptr, unsigned n) +{ + unsigned i; + uint8_t *cptr; + + cptr = sptr; + if (n % 2 != 0) + PDBG (pixma_dbg (3, "Warning: Odd number of bytes received, misaligned image.\n")); + for (i = 0; i < n; i += 2) + { + sptr++; + *cptr++ = *sptr++; + } + return (n / 2); +} + +/* This function deals both with PIXMA CCD sensors producing shifted color + * planes images, Grayscale CCD scan and Generation 3 high dpi images. + * Each complete line in mp->imgbuf is processed for shifting CCD sensor + * color planes, reordering pixels above 600 dpi for Generation 3, and + * converting to Grayscale for CCD sensors. */ +static unsigned +post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + unsigned c, lines, i, line_size, n, m; + uint8_t *sptr, *dptr, *gptr; + + c = (is_ccd_grayscale (s)) ? 3 : s->param->channels; + + if (mp->generation == 3) + n = s->param->xdpi / 600; + else /* FIXME: maybe need different values for CIS and CCD sensors */ + n = s->param->xdpi / 2400; + if (s->cfg->pid == MP970_PID) + n = MIN (n, 4); + + m = (n > 0) ? s->param->w / n : 1; + sptr = dptr = gptr = mp->imgbuf; + line_size = get_cis_ccd_line_size (s); + + lines = (mp->data_left_ofs - mp->imgbuf) / line_size; + if (lines > 2 * mp->color_shift + mp->stripe_shift) + { + lines -= 2 * mp->color_shift + mp->stripe_shift; + for (i = 0; i < lines; i++, sptr += line_size) + { + /* Color plane and stripes shift needed by e.g. CCD */ + if (c == 3) + dptr = shift_colors (dptr, sptr, + s->param->w, s->param->xdpi, s->cfg->pid, + mp->shift, mp->stripe_shift); + + /* special image format for *most* devices at high dpi. + * MP220 is a gen3 exception */ + if (s->cfg->pid != MP220_PID && n > 0) + reorder_pixels (mp->linebuf, sptr, c, n, m, s->param->w, line_size); + + /* MP970 specific reordering for 4800 dpi */ + if (s->cfg->pid == MP970_PID && s->param->xdpi == 4800) + mp970_reorder_pixels (mp->linebuf, sptr, c, s->param->w, line_size); + + /* Color to Grayscale convert for CCD sensor */ + if (is_ccd_grayscale (s)) + gptr = rgb_to_gray (gptr, sptr, s->param->w); + } + } + ib->rptr = mp->imgbuf; + ib->rend = (is_ccd_grayscale (s)) ? gptr : sptr; + return mp->data_left_ofs - sptr; /* # of non processed bytes */ +} + static int mp150_open (pixma_t * s) { @@ -776,8 +990,13 @@ mp150_open (pixma_t * s) if (s->cfg->pid == MP140_PID) mp->generation = 2; + /* TPU info data setup */ + mp->tpu_datalen = 0; + query_status (s); handle_interrupt (s, 200); + if (mp->generation == 3 && has_ccd_sensor (s)) + send_cmd_start_calibrate_ccd_3 (s); return 0; } @@ -798,25 +1017,24 @@ mp150_check_param (pixma_t * s, pixma_scan_param_t * sp) mp150_t *mp = (mp150_t *) s->subdriver; sp->depth = 8; /* MP150 only supports 8 bit per channel. */ +/* if (mp->generation == 3 && sp->source == PIXMA_SOURCE_TPU) + sp->depth = 16; */ /* TPU in 16 bits mode */ + if (mp->generation >= 2) { /* mod 32 and expansion of the X scan limits */ sp->w += (sp->x) % 32; sp->w = calc_raw_width (mp, sp); sp->x = ALIGN_INF (sp->x, 32); - /* FIXME: TBC, if all gen2 and gen3 devices do not need - * modulo 32 alignment on Y axis. If needed, uncomment next 2 lines */ - /* sp->h += (sp->y) % 32; - sp->y = ALIGN_INF (sp->y, 32);*/ } - sp->line_size = calc_raw_width (mp, sp) * sp->channels; + sp->line_size = calc_raw_width (mp, sp) * sp->channels * (sp->depth / 8); return 0; } static int mp150_scan (pixma_t * s) { - int error = 0, tmo; + int error = 0, tmo, i; mp150_t *mp = (mp150_t *) s->subdriver; if (mp->state != state_idle) @@ -833,89 +1051,85 @@ mp150_scan (pixma_t * s) if ((error = query_status (s)) < 0) return error; tmo = 10; - while (!has_paper(s) && --tmo >= 0) + while (!has_paper (s) && --tmo >= 0) { - error = handle_interrupt (s, 1000); - if (s->cancel) - return PIXMA_ECANCELED; - if (error != PIXMA_ECANCELED && error < 0) - return error; + WAIT_INTERRUPT (1000); PDBG (pixma_dbg (2, "No paper in ADF. Timed out in %d sec.\n", tmo)); } - if (!has_paper(s)) + if (!has_paper (s)) return PIXMA_ENO_PAPER; } - if (has_ccd_sensor (s)) + if (has_ccd_sensor (s) && (mp->generation <= 2)) { - error = (mp->generation <= 2) ? send_cmd_e920 (s) - : send_cmd_start_calibrate_ccd_3 (s); + error = send_cmd_e920 (s); switch (error) { case PIXMA_ECANCELED: case PIXMA_EBUSY: - PDBG (pixma_dbg - (2, "cmd e920 or d520 returned %s\n", pixma_strerror (error))); - /* fall through */ + PDBG (pixma_dbg (2, "cmd e920 or d520 returned %s\n", + pixma_strerror (error))); + /* fall through */ case 0: - query_status (s); + query_status (s); break; default: - PDBG (pixma_dbg - (1, "WARNING:cmd e920 or d520 failed %s\n", pixma_strerror (error))); + PDBG (pixma_dbg (1, "WARNING:cmd e920 or d520 failed %s\n", + pixma_strerror (error))); return error; } tmo = 3; /* like Windows driver, CCD calibration ? */ while (--tmo >= 0) { - error = handle_interrupt (s, 1000); - if (s->cancel) - return PIXMA_ECANCELED; - if (error != PIXMA_ECANCELED && error < 0) - return error; - PDBG (pixma_dbg - (2, "CCD Calibration ends in %d sec.\n", tmo)); + WAIT_INTERRUPT (1000); + PDBG (pixma_dbg (2, "CCD Calibration ends in %d sec.\n", tmo)); } /* pixma_sleep(2000000); */ } tmo = 10; - error = start_session (s); - while (error == PIXMA_EBUSY && --tmo >= 0) + if (s->param->adf_pageid == 0 || mp->generation <= 2) { - if (s->cancel) - { - error = PIXMA_ECANCELED; - break; - } - PDBG (pixma_dbg - (2, "Scanner is busy. Timed out in %d sec.\n", tmo + 1)); - pixma_sleep (1000000); - error = start_session (s); - } - if (error == PIXMA_EBUSY || error == PIXMA_ETIMEDOUT) - { - /* The scanner maybe hangs. We try to empty output buffer of the - * scanner and issue the cancel command. */ - PDBG (pixma_dbg (2, "Scanner hangs? Sending abort_session command.\n")); - drain_bulk_in (s); - abort_session (s); - pixma_sleep (500000); error = start_session (s); + while (error == PIXMA_EBUSY && --tmo >= 0) + { + if (s->cancel) + { + error = PIXMA_ECANCELED; + break; + } + PDBG (pixma_dbg + (2, "Scanner is busy. Timed out in %d sec.\n", tmo + 1)); + pixma_sleep (1000000); + error = start_session (s); + } + if (error == PIXMA_EBUSY || error == PIXMA_ETIMEDOUT) + { + /* The scanner maybe hangs. We try to empty output buffer of the + * scanner and issue the cancel command. */ + PDBG (pixma_dbg (2, "Scanner hangs? Sending abort_session command.\n")); + drain_bulk_in (s); + abort_session (s); + pixma_sleep (500000); + error = start_session (s); + } + if ((error >= 0) || (mp->generation == 3)) + mp->state = state_warmup; + if ((error >= 0) && (mp->generation <= 2)) + error = select_source (s); + if ((error >= 0) && (mp->generation == 3) && has_ccd_sensor (s)) + error = init_ccd_lamp_3 (s); + if ((error >= 0) && !is_scanning_from_tpu (s)) + for (i = (mp->generation == 3) ? 3 : 1 ; i > 0 && error >= 0; i--) + error = send_gamma_table (s); + else if (mp->generation == 3) /* FIXME: Does this apply also to gen2 ? */ + error = send_set_tpu_info_3 (s); } + else /* ADF pageid != 0 and gen3 */ + pixma_sleep (1000000); if ((error >= 0) || (mp->generation == 3)) - mp->state = state_warmup; - if ((error >= 0) && (mp->generation <= 2)) - error = select_source (s); - if ((error >= 0) && (mp->generation == 3) && has_ccd_sensor (s)) - error = init_ccd_3 (s); - if (error >= 0) - error = send_gamma_table (s); - if ((error >= 0) && (mp->generation == 3)) - error = send_gamma_table (s); - if ((error >= 0) && (mp->generation == 3)) - error = send_gamma_table (s); + mp->state = state_warmup; if (error >= 0) error = send_scan_param (s); if ((error >= 0) && (mp->generation == 3)) @@ -928,98 +1142,6 @@ mp150_scan (pixma_t * s) return 0; } -static uint8_t * -shift_colors (uint8_t * dptr, uint8_t * sptr, unsigned w, int sr, int sg, int sb) -{ - unsigned i; - - for (i = 0; i < w; i++) - { - *sptr++ = *(dptr++ + sr); - *sptr++ = *(dptr++ + sg); - *sptr++ = *(dptr++ + sb); - } - return dptr; -} - -static uint8_t * -rgb_to_gray (uint8_t * gptr, uint8_t * sptr, unsigned w) -{ - unsigned i, g; - - for (i = 0; i < w; i++) - { - g = *sptr++; - g += *sptr++; - *gptr++ = (*sptr++ + g) / 3; - } - return gptr; -} - -static void -reorder_pixels (uint8_t * linebuf, uint8_t * sptr, unsigned c, unsigned n, - unsigned m, unsigned w, unsigned line_size) -{ - unsigned i; - - for (i = 0; i < w; i++) - { - memcpy (linebuf + c * (n * (i % m) + i / m), sptr + c * i, c); - } - memcpy (sptr, linebuf, line_size); -} - -/* This function deals both with PIXMA CCD sensors producing shifted color - * planes images, Grayscale CCD scan and Generation 3 high dpi images. - * Each complete line in mp->imgbuf is processed for shifting CCD sensor - * color planes, reordering pixels above 600 dpi for Generation 3, and - * converting to Grayscale for CCD sensors. */ -static unsigned -post_process_image_data (pixma_t * s, pixma_imagebuf_t * ib) -{ - mp150_t *mp = (mp150_t *) s->subdriver; - unsigned c, lines, i, line_size, n, m; - uint8_t *sptr, *dptr, *gptr; - - c = (is_ccd_grayscale (s)) ? 3 : s->param->channels; - - if (mp->generation == 3) - n = s->param->xdpi / 600; - else /* FIXME: maybe need different values for CIS and CCD sensors */ - n = s->param->xdpi / 2400; - if (s->cfg->pid == MP970_PID) - n = MIN (n, 4); - - m = (n > 0) ? s->param->w / n : 1; - sptr = dptr = gptr = mp->imgbuf; - line_size = get_cis_ccd_line_size (s); - - lines = (mp->data_left_ofs - mp->imgbuf) / line_size; - if (lines > 2 * mp->lines_shift) - { - lines -= 2 * mp->lines_shift; - for (i = 0; i < lines; i++, sptr += line_size) - { - /* Color plane shift needed by e.g. CCD */ - if (c == 3) - dptr = shift_colors (dptr, sptr, s->param->w, - mp->shift[0], mp->shift[1], mp->shift[2]); - - /* special image format for *most* devices at high dpi. - * MP220 is a gen3 exception */ - if (s->cfg->pid != MP220_PID && n > 0) - reorder_pixels (mp->linebuf, sptr, c, n, m, s->param->w, line_size); - - /* Color to Grayscale convert for CCD sensor */ - if (is_ccd_grayscale (s)) - gptr = rgb_to_gray (gptr, sptr, s->param->w); - } - } - ib->rptr = mp->imgbuf; - ib->rend = (is_ccd_grayscale (s)) ? gptr : sptr; - return mp->data_left_ofs - sptr; /* # of non processed bytes */ -} - static int mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) { @@ -1030,9 +1152,7 @@ mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) if (mp->state == state_warmup) { - error = wait_until_ready (s); - if (error < 0) - return error; + RET_IF_ERR (wait_until_ready (s)); pixma_sleep (1000000); /* No need to sleep, actually, but Window's driver * sleep 1.5 sec. */ mp->state = state_scanning; @@ -1043,7 +1163,7 @@ mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) mp->cb.buf = realloc (mp->cb.buf, CMDBUF_SIZE + IMAGE_BLOCK_SIZE + proc_buf_size); if (!mp->cb.buf) - return PIXMA_ENOMEM; + return PIXMA_ENOMEM; mp->linebuf = mp->cb.buf + CMDBUF_SIZE; mp->imgbuf = mp->data_left_ofs = mp->linebuf + line_size; mp->data_left_len = 0; @@ -1052,41 +1172,44 @@ mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) do { if (s->cancel) - return PIXMA_ECANCELED; + return PIXMA_ECANCELED; if ((mp->last_block & 0x28) == 0x28) - { - /* end of image */ - mp->state = state_finished; - return 0; - } + { /* end of image */ + mp->state = state_finished; + return 0; + } memmove (mp->imgbuf, mp->data_left_ofs, mp->data_left_len); error = read_image_block (s, header, mp->imgbuf + mp->data_left_len); if (error < 0) - { - if (error == PIXMA_ECANCELED) - { - /* NOTE: I see this in traffic logs but I don't know its meaning. */ - read_error_info (s, NULL, 0); - } - return error; - } + { + if (error == PIXMA_ECANCELED) + { + /* NOTE: I see this in traffic logs but I don't know its meaning. */ + read_error_info (s, NULL, 0); + } + return error; + } bytes_received = error; block_size = pixma_get_be32 (header + 12); mp->last_block = header[8] & 0x38; if ((header[8] & ~0x38) != 0) - { - PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n")); - PDBG (pixma_hexdump (1, header, 16)); - } + { + PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n")); + PDBG (pixma_hexdump (1, header, 16)); + } PASSERT (bytes_received == block_size); if (block_size == 0) - { - /* no image data at this moment. */ - pixma_sleep (10000); - } + { /* no image data at this moment. */ + pixma_sleep (10000); + } + /* In case of TPU, at 48 bits/col scan, pack the newly received data */ + if (mp->generation == 3 && is_scanning_from_tpu (s)) + bytes_received = pack_48_24_bpc (mp->imgbuf + mp->data_left_len, bytes_received); + + /* Post-process the image data */ mp->data_left_ofs = mp->imgbuf + mp->data_left_len + bytes_received; mp->data_left_len = post_process_image_data (s, ib); mp->data_left_ofs -= mp->data_left_len; @@ -1109,17 +1232,20 @@ mp150_finish_scan (pixma_t * s) /* fall through */ case state_scanning: case state_warmup: - error = abort_session (s); - if (error < 0) - PDBG (pixma_dbg (1, "WARNING:abort_session() failed %d\n", error)); - /* fall through */ case state_finished: + /* For gen3 TPU , send the get TPU info message */ + if (mp->generation == 3 && is_scanning_from_tpu (s) && mp->tpu_datalen == 0) + send_get_tpu_info_3 (s); /* FIXME: to process several pages ADF scan, must not send * abort_session and start_session between pages (last_block=0x28) * if (mp->last_block != 0x28 || !is_scanning_from_adf(s)) */ - if (mp->last_block != 0x38) - abort_session (s); /* FIXME: it probably doesn't work in duplex mode! */ - mp->state = state_idle; + if (mp->generation <= 2 || !is_scanning_from_adf (s) || mp->last_block == 0x38) + { + error = abort_session (s); /* FIXME: it probably doesn't work in duplex mode! */ + if (error < 0) + PDBG (pixma_dbg (1, "WARNING:abort_session() failed %d\n", error)); + mp->state = state_idle; + } /* fall through */ case state_idle: break; @@ -1141,9 +1267,7 @@ mp150_get_status (pixma_t * s, pixma_device_status_t * status) { int error; - error = query_status (s); - if (error < 0) - return error; + RET_IF_ERR (query_status (s)); status->hardware = PIXMA_HARDWARE_OK; status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER; status->cal = @@ -1162,15 +1286,15 @@ static const pixma_scan_ops_t pixma_mp150_ops = { mp150_get_status }; -#define DEVICE(name, pid, dpi, cap) { \ - name, /* name */ \ - CANON_VID, pid, /* vid pid */ \ - 0, /* iface */ \ - &pixma_mp150_ops, /* ops */ \ - dpi, 2*(dpi), /* xdpi, ydpi */ \ - 638, 877, /* width, height */ \ - PIXMA_CAP_EASY_RGB|PIXMA_CAP_GRAY| \ - PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \ +#define DEVICE(name, pid, dpi, cap) { \ + name, /* name */ \ + CANON_VID, pid, /* vid pid */ \ + 0, /* iface */ \ + &pixma_mp150_ops, /* ops */ \ + dpi, 2*(dpi), /* xdpi, ydpi */ \ + 638, 877, /* width, height */ \ + PIXMA_CAP_EASY_RGB|PIXMA_CAP_GRAY| \ + PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \ } #define END_OF_DEVICE_LIST DEVICE(NULL, 0, 0, 0) diff --git a/doc/descriptions/pixma.desc b/doc/descriptions/pixma.desc index c84898043..f52d94cfd 100644 --- a/doc/descriptions/pixma.desc +++ b/doc/descriptions/pixma.desc @@ -158,7 +158,7 @@ :interface "USB" :usbid "0x04a9" "0x1713" :status :basic -:comment "Single-side ADF works but duplex doesn't work yet." +:comment "Single-side ADF works, Duplex ADF to be tested." :model "PIXMA MP960" :interface "USB" @@ -169,7 +169,7 @@ :interface "USB" :usbid "0x04a9" "0x1726" :status :good -:comment "All resolutions supported (up to 4800DPI)" +:comment "All resolutions supported (up to 4800DPI). TPU support currently experimental." :model "SmartBase MP360" :interface "USB" @@ -240,8 +240,8 @@ :model "PIXMA MX7600" :interface "USB" :usbid "0x04a9" "0x171c" -:status :untested -:comment "Generation 3 protocol? Testers needed!" +:status :good +:comment "Flatbed and ADF scan. All resolutions supported (up to 4800DPI)" :model "imageCLASS MF5630" :interface "USB" diff --git a/doc/sane-pixma.man b/doc/sane-pixma.man index c4dccc932..1f93d4051 100644 --- a/doc/sane-pixma.man +++ b/doc/sane-pixma.man @@ -18,7 +18,7 @@ PIXMA MP600, MP600R, MP610, MP710 .br PIXMA MP800, MP800R, MP810, MP830, MP960, MP970 .br -PIXMA MX300, MX310, MX700 +PIXMA MX300, MX310, MX700, MX850, MX7600 .br MultiPASS MP700, PIXMA MP750 (no grayscale) .br @@ -47,13 +47,13 @@ ImageCLASS MF3110, MF3240 ImageCLASS MF5630, MF5650, MF5730, MF5750, MF5770, MF8170c .RE .PP -The following models may use partly the same Pixma protocol as MPs listed -above, but may still need some work. They are declared in the backend as -experimental. Snoop logs are required to further investigate, please contact -the sane\-devel mailing list. +\#The following models may use partly the same Pixma protocol as MPs listed +\#above, but may still need some work. They are declared in the backend as +\#experimental. Snoop logs are required to further investigate, please contact +\#the sane\-devel mailing list. .PP .RS -PIXMA MX850 +\#PIXMA MX850 .RE .PP The backend supports @@ -64,7 +64,9 @@ The backend supports .br * a custom gamma table and .br -* automatic document feeder (only single side, duplex needs some work). +* Automatic Document Feeder (Duplex for some models). +.br +* Transparency Unit support is still experimental. .PP The device name is in the form pixma:xxxxyyyy_zzzzz where x, y and z are vendor ID, product ID and serial number respectively.