From ffa8801644a7d53cc1c785e3450f794c07a14eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 2 Feb 2020 17:13:01 +0100 Subject: New upstream version 1.0.29 --- backend/pixma/pixma_imageclass.c | 985 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 985 insertions(+) create mode 100644 backend/pixma/pixma_imageclass.c (limited to 'backend/pixma/pixma_imageclass.c') diff --git a/backend/pixma/pixma_imageclass.c b/backend/pixma/pixma_imageclass.c new file mode 100644 index 0000000..ce0c37d --- /dev/null +++ b/backend/pixma/pixma_imageclass.c @@ -0,0 +1,985 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2011-2019 Rolf Bensch + Copyright (C) 2007-2009 Nicolas Martin, + Copyright (C) 2008 Dennis Lou, dlou 99 at yahoo dot com + + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. + */ + +/* + * imageCLASS backend based on pixma_mp730.c + */ + +#include "../include/sane/config.h" + +#include +#include +#include + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" + + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +#define IMAGE_BLOCK_SIZE (0x80000) +#define MAX_CHUNK_SIZE (0x1000) +#define MIN_CHUNK_SIZE (0x0200) +#define CMDBUF_SIZE 512 + +#define MF4100_PID 0x26a3 +#define MF4600_PID 0x26b0 +#define MF4010_PID 0x26b4 +#define MF4200_PID 0x26b5 +#define MF4360_PID 0x26ec +#define D480_PID 0x26ed +#define MF4320_PID 0x26ee +#define D420_PID 0x26ef +#define MF3200_PID 0x2684 +#define MF6500_PID 0x2686 +/* generation 2 scanners (>=0x2707) */ +#define MF8300_PID 0x2708 +#define MF4500_PID 0x2736 +#define MF4410_PID 0x2737 +#define D550_PID 0x2738 +#define MF3010_PID 0x2759 +#define MF4570_PID 0x275a +#define MF4800_PID 0x2773 +#define MF4700_PID 0x2774 +#define MF8200_PID 0x2779 +/* the following are all untested */ +#define MF5630_PID 0x264e +#define MF5650_PID 0x264f +#define MF8100_PID 0x2659 +#define MF5880_PID 0x26f9 +#define MF6680_PID 0x26fa +#define MF8030_PID 0x2707 +#define IR1133_PID 0x2742 +#define MF5900_PID 0x2743 +#define D530_PID 0x2775 +#define MF8500_PID 0x277a +#define MF6100_PID 0x278e +#define MF820_PID 0x27a6 +#define MF220_PID 0x27a8 +#define MF210_PID 0x27a9 +#define MF620_PID 0x27b4 +#define MF410_PID 0x27c0 +#define MF510_PID 0x27c2 +#define MF230_PID 0x27d1 +#define MF240_PID 0x27d2 +#define MF630_PID 0x27e1 +#define MF634_PID 0x27e2 +#define MF730_PID 0x27e4 +#define MF731_PID 0x27e5 +#define D570_PID 0x27e8 +#define MF110_PID 0x27ed +#define MF520_PID 0x27f0 +#define MF420_PID 0x27f1 +#define MF260_PID 0x27f4 +#define MF740_PID 0x27fb +#define MF743_PID 0x27fc +#define MF640_PID 0x27fe +#define MF645_PID 0x27fd + + +enum iclass_state_t +{ + state_idle, + state_warmup, /* MF4200 always warm/calibrated; others? */ + state_scanning, + state_finished +}; + +enum iclass_cmd_t +{ + cmd_start_session = 0xdb20, + cmd_select_source = 0xdd20, + cmd_scan_param = 0xde20, + cmd_status = 0xf320, + cmd_abort_session = 0xef20, + cmd_read_image = 0xd420, + cmd_read_image2 = 0xd460, /* New multifunctionals, such as MF4410 */ + cmd_error_info = 0xff20, + + cmd_activate = 0xcf60 +}; + +typedef struct iclass_t +{ + enum iclass_state_t state; + pixma_cmdbuf_t cb; + unsigned raw_width; + uint8_t current_status[12]; + + uint8_t *buf, *blkptr, *lineptr; + unsigned buf_len, blk_len; + + unsigned last_block; + + uint8_t generation; /* New multifunctionals are (generation == 2) */ + + uint8_t adf_state; /* handle adf scanning */ +} iclass_t; + + +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 void iclass_finish_scan (pixma_t * s); + +/* checksumming is sometimes different than pixmas */ +static int +iclass_exec (pixma_t * s, pixma_cmdbuf_t * cb, char invcksum) +{ + if (cb->cmdlen > cb->cmd_header_len) + pixma_fill_checksum (cb->buf + cb->cmd_header_len, + cb->buf + cb->cmdlen - 2); + cb->buf[cb->cmdlen - 1] = invcksum ? -cb->buf[cb->cmdlen - 2] : 0; + cb->reslen = + pixma_cmd_transaction (s, cb->buf, cb->cmdlen, cb->buf, + cb->expected_reslen); + return pixma_check_result (cb); +} + +static int +has_paper (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + return ((mf->current_status[1] & 0x0f) == 0 /* allow 0x10 as ADF paper OK */ + || mf->current_status[1] == 81); /* allow 0x51 as ADF paper OK */ +} + +static int +abort_session (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mf->cb, cmd_abort_session); +} + +static int +query_status (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mf->cb, cmd_status, 0, 12); + error = pixma_exec (s, &mf->cb); + if (error >= 0) + { + memcpy (mf->current_status, data, 12); + /*DBG (3, "Current status: paper=0x%02x cal=%u lamp=%u\n", + data[1], data[8], data[7]);*/ + PDBG (pixma_dbg (3, "Current status: paper=0x%02x cal=%u lamp=%u\n", + data[1], data[8], data[7])); + } + return error; +} + +static int +activate (pixma_t * s, uint8_t x) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + uint8_t *data = pixma_newcmd (&mf->cb, cmd_activate, 10, 0); + data[0] = 1; + data[3] = x; + switch (s->cfg->pid) + { + case MF4200_PID: + case MF4600_PID: + case MF6500_PID: + case D480_PID: + case D420_PID: + case MF4360_PID: + case MF4100_PID: + case MF8300_PID: + return iclass_exec (s, &mf->cb, 1); + break; + default: + return pixma_exec (s, &mf->cb); + } +} + +static int +start_session (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mf->cb, cmd_start_session); +} + +static int +select_source (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + uint8_t *data = pixma_newcmd (&mf->cb, cmd_select_source, 10, 0); + data[0] = (is_scanning_from_adf(s)) ? 2 : 1; + /* special settings for MF6100 */ + data[5] = is_scanning_from_adfdup(s) ? 3 : ((s->cfg->pid == MF6100_PID && s->param->source == PIXMA_SOURCE_ADF) ? 1 : 0); + switch (s->cfg->pid) + { + case MF4200_PID: + case MF4600_PID: + case MF6500_PID: + case D480_PID: + case D420_PID: + case MF4360_PID: + case MF4100_PID: + case MF8300_PID: + return iclass_exec (s, &mf->cb, 0); + break; + default: + return pixma_exec (s, &mf->cb); + } +} + +static int +send_scan_param (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + uint8_t *data; + + data = pixma_newcmd (&mf->cb, cmd_scan_param, 0x2e, 0); + pixma_set_be16 (s->param->xdpi | 0x1000, data + 0x04); + pixma_set_be16 (s->param->ydpi | 0x1000, data + 0x06); + pixma_set_be32 (s->param->x, data + 0x08); + pixma_set_be32 (s->param->y, data + 0x0c); + pixma_set_be32 (mf->raw_width, data + 0x10); + pixma_set_be32 (s->param->h, data + 0x14); + data[0x18] = (s->param->channels == 1) ? 0x04 : 0x08; + data[0x19] = s->param->channels * ((s->param->depth == 1) ? 8 : s->param->depth); /* bits per pixel */ + data[0x1f] = 0x7f; + data[0x20] = 0xff; + data[0x23] = 0x81; + switch (s->cfg->pid) + { + case MF4200_PID: + case MF4600_PID: + case MF6500_PID: + case D480_PID: + case D420_PID: + case MF4360_PID: + case MF4100_PID: + case MF8300_PID: + return iclass_exec (s, &mf->cb, 0); + break; + default: + return pixma_exec (s, &mf->cb); + } +} + +static int +request_image_block (pixma_t * s, unsigned flag, uint8_t * info, + unsigned * size, uint8_t * data, unsigned * datalen) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + int error; + unsigned expected_len; + const int hlen = 2 + 6; + + memset (mf->cb.buf, 0, 11); + /* generation 2 scanners use cmd_read_image2. + * MF6100, ... are exceptions */ + pixma_set_be16 (((mf->generation >= 2 + && s->cfg->pid != MF6100_PID) ? cmd_read_image2 : cmd_read_image), mf->cb.buf); + mf->cb.buf[8] = flag; + mf->cb.buf[10] = 0x06; + expected_len = (mf->generation >= 2 || + s->cfg->pid == MF4600_PID || + s->cfg->pid == MF6500_PID || + s->cfg->pid == MF8030_PID) ? 512 : hlen; + mf->cb.reslen = pixma_cmd_transaction (s, mf->cb.buf, 11, mf->cb.buf, expected_len); + if (mf->cb.reslen >= hlen) + { + *info = mf->cb.buf[2]; + *size = pixma_get_be16 (mf->cb.buf + 6); /* 16bit size */ + error = 0; + + if (mf->generation >= 2 || + s->cfg->pid == MF4600_PID || + s->cfg->pid == MF6500_PID || + s->cfg->pid == MF8030_PID) + { /* 32bit size */ + *datalen = mf->cb.reslen - hlen; + *size = (*datalen + hlen == 512) ? pixma_get_be32 (mf->cb.buf + 4) - *datalen : *size; + memcpy (data, mf->cb.buf + hlen, *datalen); + } + PDBG (pixma_dbg (11, "*request_image_block***** size = %u *****\n", *size)); + } + else + { + error = PIXMA_EPROTO; + } + return error; +} + +static int +read_image_block (pixma_t * s, uint8_t * data, unsigned size) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + int error; + unsigned maxchunksize, chunksize, count = 0; + + maxchunksize = MAX_CHUNK_SIZE * ((mf->generation >= 2 || + s->cfg->pid == MF4600_PID || + s->cfg->pid == MF6500_PID || + s->cfg->pid == MF8030_PID) ? 4 : 1); + while (size) + { + if (size >= maxchunksize) + chunksize = maxchunksize; + else if (size < MIN_CHUNK_SIZE) + chunksize = size; + else + chunksize = size - (size % MIN_CHUNK_SIZE); + error = pixma_read (s->io, data, chunksize); + if (error < 0) + return count; + count += error; + data += error; + size -= error; + } + return count; +} + +static int +read_error_info (pixma_t * s, void *buf, unsigned size) +{ + unsigned len = 16; + iclass_t *mf = (iclass_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mf->cb, cmd_error_info, 0, len); + switch (s->cfg->pid) + { + case MF4200_PID: + case MF4600_PID: + case MF6500_PID: + case D480_PID: + case D420_PID: + case MF4360_PID: + case MF4100_PID: + case MF8300_PID: + error = iclass_exec (s, &mf->cb, 0); + break; + default: + error = pixma_exec (s, &mf->cb); + } + if (error < 0) + return error; + if (buf && len < size) + { + size = len; + /* NOTE: I've absolutely no idea what the returned data mean. */ + memcpy (buf, data, size); + error = len; + } + return error; +} + +static int +handle_interrupt (pixma_t * s, int timeout) +{ + uint8_t buf[16]; + int len; + + len = pixma_wait_interrupt (s->io, buf, sizeof (buf), timeout); + if (len == PIXMA_ETIMEDOUT) + return 0; + if (len < 0) + return len; + if (len != 16) + { + PDBG (pixma_dbg + (1, "WARNING:unexpected interrupt packet length %d\n", len)); + return PIXMA_EPROTO; + } + if (buf[12] & 0x40) + query_status (s); + if (buf[15] & 1) + s->events = PIXMA_EV_BUTTON1; + return 1; +} + +static int +step1 (pixma_t * s) +{ + int error; + int rec_tmo; + iclass_t *mf = (iclass_t *) s->subdriver; + + /* don't wait full timeout for 1st command */ + rec_tmo = s->rec_tmo; /* save globel timeout */ + s->rec_tmo = 2; /* set timeout to 2 seconds */ + error = query_status (s); + s->rec_tmo = rec_tmo; /* restore global timeout */ + if (error < 0) + { + PDBG (pixma_dbg (1, "WARNING: Resend first USB command after timeout!\n")); + error = query_status (s); + } + if (error < 0) + return error; + + /* wait for inserted paper */ + if (s->param->adf_wait != 0 && is_scanning_from_adf(s)) + { + int tmo = s->param->adf_wait; + + while (!has_paper (s) && --tmo >= 0 && !s->param->frontend_cancel) + { + if ((error = query_status (s)) < 0) + return error; + pixma_sleep (1000000); + PDBG (pixma_dbg(2, "No paper in ADF. Timed out in %d sec.\n", tmo)); + } + /* canceled from frontend */ + if (s->param->frontend_cancel) + { + return PIXMA_ECANCELED; + } + } + /* no paper inserted + * => abort session */ + if (is_scanning_from_adf(s) && !has_paper (s)) + { + return PIXMA_ENO_PAPER; + } + /* activate only seen for generation 1 scanners */ + if (mf->generation == 1) + { + if (error >= 0) + error = activate (s, 0); + if (error >= 0) + error = activate (s, 4); + } + return error; +} + +/* line in=rrr... ggg... bbb... line out=rgbrgbrgb... */ +static void +pack_rgb (const uint8_t * src, unsigned nlines, unsigned w, uint8_t * dst) +{ + unsigned w2, stride; + + w2 = 2 * w; + stride = 3 * w; + for (; nlines != 0; nlines--) + { + unsigned x; + for (x = 0; x != w; x++) + { + *dst++ = src[x + 0]; + *dst++ = src[x + w]; + *dst++ = src[x + w2]; + } + src += stride; + } +} + +static int +iclass_open (pixma_t * s) +{ + iclass_t *mf; + uint8_t *buf; + + mf = (iclass_t *) calloc (1, sizeof (*mf)); + if (!mf) + return PIXMA_ENOMEM; + + buf = (uint8_t *) malloc (CMDBUF_SIZE); + if (!buf) + { + free (mf); + return PIXMA_ENOMEM; + } + + s->subdriver = mf; + mf->state = state_idle; + + mf->cb.buf = buf; + mf->cb.size = CMDBUF_SIZE; + mf->cb.res_header_len = 2; + mf->cb.cmd_header_len = 10; + mf->cb.cmd_len_field_ofs = 7; + + /* adf scanning */ + mf->adf_state = state_idle; + + /* set generation = 2 for new multifunctionals */ + mf->generation = (s->cfg->pid >= MF8030_PID) ? 2 : 1; + PDBG (pixma_dbg (3, "*iclass_open***** This is a generation %d scanner. *****\n", mf->generation)); + + PDBG (pixma_dbg (3, "Trying to clear the interrupt buffer...\n")); + if (handle_interrupt (s, 200) == 0) + { + PDBG (pixma_dbg (3, " no packets in buffer\n")); + } + return 0; +} + +static void +iclass_close (pixma_t * s) +{ + iclass_t *mf = (iclass_t *) s->subdriver; + + iclass_finish_scan (s); + free (mf->cb.buf); + free (mf->buf); + free (mf); + s->subdriver = NULL; +} + +static int +iclass_check_param (pixma_t * s, pixma_scan_param_t * sp) +{ + UNUSED (s); + + /* PDBG (pixma_dbg (4, "*iclass_check_param***** Initially: channels=%u, depth=%u, x=%u, y=%u, w=%u, line_size=%" PRIu64 " , h=%u*****\n", + sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->line_size, sp->h)); */ + + sp->depth = 8; + sp->software_lineart = 0; + if (sp->mode == PIXMA_SCAN_MODE_LINEART) + { + sp->software_lineart = 1; + sp->channels = 1; + sp->depth = 1; + } + + if (sp->software_lineart == 1) + { + unsigned w_max; + + /* for software lineart line_size and w must be a multiple of 8 */ + sp->line_size = ALIGN_SUP (sp->w, 8) * sp->channels; + sp->w = ALIGN_SUP (sp->w, 8); + + /* do not exceed the scanner capability */ + w_max = s->cfg->width * s->cfg->xdpi / 75; + w_max -= w_max % 32; + if (sp->w > w_max) + sp->w = w_max; + } + else + sp->line_size = ALIGN_SUP (sp->w, 32) * sp->channels; + + /* Some exceptions here for particular devices */ + /* Those devices can scan up to Legal 14" with ADF, but A4 11.7" in flatbed */ + /* PIXMA_CAP_ADF also works for PIXMA_CAP_ADFDUP */ + if ((s->cfg->cap & PIXMA_CAP_ADF) && sp->source == PIXMA_SOURCE_FLATBED) + sp->h = MIN (sp->h, 877 * sp->xdpi / 75); + + /* PDBG (pixma_dbg (4, "*iclass_check_param***** Finally: channels=%u, depth=%u, x=%u, y=%u, w=%u, line_size=%" PRIu64 " , h=%u*****\n", + sp->channels, sp->depth, sp->x, sp->y, sp->w, sp->line_size, sp->h)); */ + + return 0; +} + +static int +iclass_scan (pixma_t * s) +{ + int error, n; + iclass_t *mf = (iclass_t *) s->subdriver; + uint8_t *buf, ignore; + unsigned buf_len, ignore2; + + if (mf->state != state_idle) + return PIXMA_EBUSY; + + /* clear interrupt packets buffer */ + while (handle_interrupt (s, 0) > 0) + { + } + + mf->raw_width = ALIGN_SUP (s->param->w, 32); + PDBG (pixma_dbg (3, "raw_width = %u\n", mf->raw_width)); + + n = IMAGE_BLOCK_SIZE / s->param->line_size + 1; + buf_len = (n + 1) * s->param->line_size + IMAGE_BLOCK_SIZE; + if (buf_len > mf->buf_len) + { + buf = (uint8_t *) realloc (mf->buf, buf_len); + if (!buf) + return PIXMA_ENOMEM; + mf->buf = buf; + mf->buf_len = buf_len; + } + mf->lineptr = mf->buf; + mf->blkptr = mf->buf + n * s->param->line_size; + mf->blk_len = 0; + + error = step1 (s); + if (error >= 0 + && (s->param->adf_pageid == 0 || mf->generation == 1 || mf->adf_state == state_idle)) + { /* single sheet or first sheet from ADF */ + PDBG (pixma_dbg (3, "*iclass_scan***** start scanning *****\n")); + error = start_session (s); + if (error >= 0) + mf->state = state_scanning; + if (error >= 0) + error = select_source (s); + } + else if (error >= 0) + { /* next sheet from ADF */ + PDBG (pixma_dbg (3, "*iclass_scan***** scan next sheet from ADF *****\n")); + mf->state = state_scanning; + } + if (error >= 0) + error = send_scan_param (s); + if (error >= 0) + error = request_image_block (s, 0, &ignore, &ignore2, &ignore, &ignore2); + if (error < 0) + { + iclass_finish_scan (s); + return error; + } + mf->last_block = 0; + + /* ADF scanning active */ + if (is_scanning_from_adf (s)) + mf->adf_state = state_scanning; + return 0; +} + + +static int +iclass_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) +{ + int error, n; + iclass_t *mf = (iclass_t *) s->subdriver; + unsigned block_size, lines_size, lineart_lines_size, first_block_size; + uint8_t info; + +/* + * 1. send a block request cmd (d4 20 00... 04 00 06) + * 2. examine the response for block size and/or end-of-scan flag + * 3. read the block one chunk at a time + * 4. repeat until have enough to process >=1 lines + */ + do + { + do + { + if (s->cancel) + return PIXMA_ECANCELED; + if (mf->last_block) + { + /* end of image */ + mf->state = state_finished; + return 0; + } + + first_block_size = 0; + error = request_image_block (s, 4, &info, &block_size, + mf->blkptr + mf->blk_len, &first_block_size); + /* add current block to remainder of previous */ + mf->blk_len += first_block_size; + if (error < 0) + { + /* NOTE: seen in traffic logs but don't know the meaning. */ + read_error_info (s, NULL, 0); + if (error == PIXMA_ECANCELED) + return error; + } + + /* info: 0x28 = end; 0x38 = end + ADF empty */ + mf->last_block = info & 0x38; + if ((info & ~0x38) != 0) + { + PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n")); + PDBG (pixma_hexdump (1, &info, 1)); + } + + if (block_size == 0) + { + /* no image data at this moment. */ + /*pixma_sleep(100000); *//* FIXME: too short, too long? */ + handle_interrupt (s, 100); + } + } + while (block_size == 0 && first_block_size == 0); + + error = read_image_block (s, mf->blkptr + mf->blk_len, block_size); + block_size = error; + if (error < 0) + return error; + + /* add current block to remainder of previous */ + mf->blk_len += block_size; + /* n = number of full lines (rows) we have in the buffer. */ + n = mf->blk_len / ((s->param->mode == PIXMA_SCAN_MODE_LINEART) ? mf->raw_width : s->param->line_size); + if (n != 0) + { + /* PDBG (pixma_dbg (4, "*iclass_fill_buffer***** Processing with n=%d, w=%i, line_size=%" PRIu64 ", raw_width=%u ***** \n", + n, s->param->w, s->param->line_size, mf->raw_width)); */ + /* PDBG (pixma_dbg (4, "*iclass_fill_buffer***** scan_mode=%d, lineptr=%" PRIu64 ", blkptr=%" PRIu64 " \n", + s->param->mode, (uint64_t)mf->lineptr, (uint64_t)mf->blkptr)); */ + + /* gray to lineart convert + * mf->lineptr : image line + * mf->blkptr : scanned image block as grayscale + * s->param->w : image width + * s->param->line_size : scanned image width */ + if (s->param->mode == PIXMA_SCAN_MODE_LINEART) + { + int i; + uint8_t *sptr, *dptr; + + /* PDBG (pixma_dbg (4, "*iclass_fill_buffer***** Processing lineart *****\n")); */ + + /* process ALL lines */ + sptr = mf->blkptr; + dptr = mf->lineptr; + for (i = 0; i < n; i++, sptr += mf->raw_width) + dptr = pixma_binarize_line (s->param, dptr, sptr, s->param->line_size, 1); + } + else if (s->param->channels != 1 && + mf->generation == 1 && + s->cfg->pid != MF4600_PID && + s->cfg->pid != MF6500_PID && + s->cfg->pid != MF8030_PID) + { + /* color and not MF46xx or MF65xx */ + pack_rgb (mf->blkptr, n, mf->raw_width, mf->lineptr); + } + else + { + /* grayscale */ + memcpy (mf->lineptr, mf->blkptr, n * s->param->line_size); + } + /* cull remainder and shift left */ + lineart_lines_size = n * s->param->line_size / 8; + lines_size = n * ((s->param->mode == PIXMA_SCAN_MODE_LINEART) ? mf->raw_width : s->param->line_size); + mf->blk_len -= lines_size; + memcpy (mf->blkptr, mf->blkptr + lines_size, mf->blk_len); + } + } + while (n == 0); + + /* output full lines, keep partial lines for next block + * ib->rptr : start of image buffer + * ib->rend : end of image buffer */ + ib->rptr = mf->lineptr; + ib->rend = mf->lineptr + (s->param->mode == PIXMA_SCAN_MODE_LINEART ? lineart_lines_size : lines_size); + /* PDBG (pixma_dbg (4, "*iclass_fill_buffer***** rptr=%" PRIu64 ", rend=%" PRIu64 ", diff=%ld \n", + (uint64_t)ib->rptr, (uint64_t)ib->rend, ib->rend - ib->rptr)); */ + return ib->rend - ib->rptr; +} + +static void +iclass_finish_scan (pixma_t * s) +{ + int error; + iclass_t *mf = (iclass_t *) s->subdriver; + + switch (mf->state) + { + /* fall through */ + case state_warmup: + case state_scanning: + error = abort_session (s); + if (error < 0) + PDBG (pixma_dbg + (1, "WARNING:abort_session() failed %s\n", + pixma_strerror (error))); + /* fall through */ + case state_finished: + query_status (s); + query_status (s); + if (mf->generation == 1) + { /* activate only seen for generation 1 scanners */ + activate (s, 0); + query_status (s); + } + /* generation = 1: + * 0x28 = last block (no multi page scan) + * generation >= 2: + * 0x38 = last block and ADF empty (generation >= 2) + * 0x28 = last block and Paper in ADF (multi page scan) + * some generation 2 scanners don't use 0x38 for ADF empty => check status */ + if (mf->last_block==0x38 /* generation 2 scanner ADF empty */ + || (mf->generation == 1 && mf->last_block == 0x28) /* generation 1 scanner last block */ + || (mf->generation >= 2 && !has_paper(s))) /* check status: no paper in ADF */ + { + /* ADFDUP scan: wait for 8sec to throw last page out of ADF feeder */ + if (is_scanning_from_adfdup(s)) + { + PDBG (pixma_dbg (4, "*iclass_finish_scan***** sleep for 8s *****\n")); + pixma_sleep(8000000); /* sleep for 8s */ + query_status (s); + } + PDBG (pixma_dbg (3, "*iclass_finish_scan***** abort session *****\n")); + abort_session (s); + mf->adf_state = state_idle; + mf->last_block = 0; + } + else + PDBG (pixma_dbg (3, "*iclass_finish_scan***** wait for next page from ADF *****\n")); + + mf->state = state_idle; + /* fall through */ + case state_idle: + break; + } +} + +static void +iclass_wait_event (pixma_t * s, int timeout) +{ + /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for + * instance. */ + while (s->events == 0 && handle_interrupt (s, timeout) > 0) + { + } +} + +static int +iclass_get_status (pixma_t * s, pixma_device_status_t * status) +{ + int error; + + error = query_status (s); + if (error < 0) + return error; + status->hardware = PIXMA_HARDWARE_OK; + status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER; + return 0; +} + + +static const pixma_scan_ops_t pixma_iclass_ops = { + iclass_open, + iclass_close, + iclass_scan, + iclass_fill_buffer, + iclass_finish_scan, + iclass_wait_event, + iclass_check_param, + iclass_get_status +}; + +#define DEV(name, model, pid, dpi, adftpu_max_dpi, w, h, cap) { \ + name, /* name */ \ + model, /* model */ \ + 0x04a9, pid, /* vid pid */ \ + 1, /* iface */ \ + &pixma_iclass_ops, /* ops */ \ + 0, /* min_xdpi not used in this subdriver */ \ + dpi, dpi, /* xdpi, ydpi */ \ + 0, /* adftpu_min_dpi not used in this subdriver */ \ + adftpu_max_dpi, /* adftpu_max_dpi */ \ + 0, 0, /* tpuir_min_dpi & tpuir_max_dpi not used in this subdriver */ \ + w, h, /* width, height */ \ + PIXMA_CAP_LINEART| /* all scanners have software lineart */ \ + PIXMA_CAP_ADF_WAIT| /* adf wait for all ADF and ADFDUP scanners */ \ + PIXMA_CAP_GRAY|PIXMA_CAP_EVENTS|cap \ +} +const pixma_config_t pixma_iclass_devices[] = { + DEV ("Canon imageCLASS MF4270", "MF4270", MF4200_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS MF4150", "MF4100", MF4100_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS MF4690", "MF4690", MF4600_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS D420", "D420", D420_PID, 600, 0, 640, 877, PIXMA_CAP_ADFDUP), + DEV ("Canon imageCLASS D480", "D480", D480_PID, 600, 0, 640, 877, PIXMA_CAP_ADFDUP), + DEV ("Canon imageCLASS MF4360", "MF4360", MF4360_PID, 600, 0, 640, 877, PIXMA_CAP_ADFDUP), + DEV ("Canon imageCLASS MF4320", "MF4320", MF4320_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS MF4010", "MF4010", MF4010_PID, 600, 0, 640, 877, 0), + DEV ("Canon imageCLASS MF3240", "MF3240", MF3200_PID, 600, 0, 640, 877, 0), + DEV ("Canon imageClass MF6500", "MF6500", MF6500_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS MF4410", "MF4410", MF4410_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS D550", "D550", D550_PID, 600, 0, 640, 1050, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF4500 Series", "MF4500", MF4500_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF3010", "MF3010", MF3010_PID, 600, 0, 640, 877, 0), + DEV ("Canon i-SENSYS MF4700 Series", "MF4700", MF4700_PID, 600, 0, 640, 1050, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF4800 Series", "MF4800", MF4800_PID, 600, 0, 640, 1050, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS MF4570dw", "MF4570dw", MF4570_PID, 600, 0, 640, 877, 0), + DEV ("Canon i-SENSYS MF8200C Series", "MF8200C", MF8200_PID, 600, 300, 640, 1050, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF8300 Series", "MF8300", MF8300_PID, 600, 0, 640, 1050, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS D530", "D530", D530_PID, 600, 0, 640, 877, 0), + /* FIXME: the following capabilities all need updating/verifying */ + DEV ("Canon imageCLASS MF5630", "MF5630", MF5630_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon laserBase MF5650", "MF5650", MF5650_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageCLASS MF8170c", "MF8170c", MF8100_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon imageClass MF8030", "MF8030", MF8030_PID, 600, 0, 640, 877, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF5880dn", "MF5880", MF5880_PID, 600, 0, 640, 877, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF6680dn", "MF6680", MF6680_PID, 600, 0, 640, 877, PIXMA_CAP_ADFDUP), + DEV ("Canon imageRUNNER 1133", "iR1133", IR1133_PID, 600, 0, 637, 877, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ + DEV ("Canon i-SENSYS MF5900 Series", "MF5900", MF5900_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF8500C Series", "MF8500C", MF8500_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF6100 Series", "MF6100", MF6100_PID, 600, 300, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon imageClass MF810/820", "MF810/820", MF820_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF220 Series", "MF220", MF220_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ + DEV ("Canon i-SENSYS MF210 Series", "MF210", MF210_PID, 600, 0, 637, 1050, PIXMA_CAP_ADF), /* max. w = 216mm */ + DEV ("Canon i-SENSYS MF620 Series", "MF620", MF620_PID, 600, 0, 637, 1050, PIXMA_CAP_ADF), + DEV ("Canon i-SENSYS MF410 Series", "MF410", MF410_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ + DEV ("Canon i-SENSYS MF510 Series", "MF510", MF510_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF230 Series", "MF230", MF230_PID, 600, 0, 637, 1050, PIXMA_CAP_ADF), /* max. w = 216mm */ + DEV ("Canon i-SENSYS MF240 Series", "MF240", MF240_PID, 600, 300, 634, 1050, PIXMA_CAP_ADF), /* max. w = 215mm, */ + /* TODO: fix black stripes for 216mm @ 600dpi */ + DEV ("Canon i-SENSYS MF630 Series", "MF630", MF630_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF730 Series", "MF730", MF730_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF731C", "MF731", MF731_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF633C/MF635C", "MF633C/635C", MF630_PID, 600, 0, 637, 877, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ + DEV ("Canon imageCLASS MF634C", "MF632C/634C", MF634_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon imageCLASS MF733C", "MF731C/733C", MF731_PID, 600, 0, 637, 1050, PIXMA_CAP_ADFDUP), /* however, we need this for ethernet/wifi */ + DEV ("Canon imageCLASS D570", "D570", D570_PID, 600, 0, 640, 877, 0), + DEV ("Canon i-SENSYS MF110 Series", "MF110", MF110_PID, 600, 0, 640, 1050, 0), + DEV ("Canon i-SENSYS MF520 Series", "MF520", MF520_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF420 Series", "MF420", MF420_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF260 Series", "MF260", MF260_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF740 Series", "MF740", MF740_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF741C/743C", "MF741C/743C", MF743_PID, 600, 300, 640, 1050, PIXMA_CAP_ADFDUP), /* ADFDUP restricted to 300dpi */ + DEV ("Canon i-SENSYS MF640 Series", "MF642C/643C/644C", MF640_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP), + DEV ("Canon i-SENSYS MF645C", "MF645C", MF645_PID, 600, 0, 637, 877, PIXMA_CAP_ADFDUP), /* max. w = 216mm */ + DEV (NULL, NULL, 0, 0, 0, 0, 0, 0) +}; -- cgit v1.2.3