Added pixma backend.

Moved doc/descriptions-external/pixma.desc to doc/descriptions
merge-requests/1/head
Wittawat Yamwong 2006-05-26 07:49:24 +00:00
rodzic a1061d821f
commit 258b7934af
21 zmienionych plików z 5708 dodań i 10 usunięć

Wyświetl plik

@ -60,6 +60,7 @@ Backends:
niash: Ullrich Sigwanz (*), Bertrik Sikken
pie: Simon Munton (*)
pint: Gordon Matzigkeit
pixma: Wittawat Yamwong (*)
plustek: Gerhard Jaeger (*)
plustek_pp: Rick Bronson (former pp driver-code), Gerhard Jaeger (*)
pnm: Andreas Beck, Gordon Matzigkeit, David Mosberger, Michael
@ -212,3 +213,4 @@ Tristan Tarrant <ttarrant@etnoteam.it>
Troy Rollo <sane@troy.rollo.name>
Ullrich Sigwanz <usigwanz@freesurf.ch>
Ulrich Deiters <ukd@xenon.pc.Uni-Koeln.DE>
Wittawat Yamwong <wittawat@web.de>

Wyświetl plik

@ -136,6 +136,10 @@ DISTFILES = abaton.c abaton.conf.in abaton.h agfafocus.c agfafocus.conf.in \
mustek_usb2_reflective.c mustek_usb2_transparent.c \
net.c net.conf.in net.h niash.c niash_core.c niash_core.h \
niash_xfer.c niash_xfer.h pie.c pie.conf.in pie-scsidef.h pint.c pint.h \
pixma.c pixma.h pixma_common.c pixma_common.h \
pixma_mp150.c pixma_mp730.c pixma_mp750.c \
pixma_sane_options.c pixma_sane_options.h pixma_io.h pixma_io_sanei.c \
pixma_rename.h \
plustek.c plustek.conf.in plustek-usbdevs.c plustek.h \
plustek-usb.c plustek-usb.h plustek-usbhw.c plustek-usbimg.c \
plustek-usbio.c plustek-usbmap.c plustek-usbscan.c plustek-usbshading.c \
@ -306,6 +310,7 @@ EXTRA_hp = hp-accessor hp-device hp-handle hp-hpmem hp-option hp-scl
EXTRA_umax_pp = umax_pp_low umax_pp_mid
EXTRA_epson = epson_scsi epson_usb
EXTRA_lexmark = lexmark-x1100
EXTRA_pixma = pixma_io_sanei pixma_common pixma_mp150 pixma_mp730 pixma_mp750
# When preloading dll, we need to add in all preloaded objects:
libsane-dll.la: $(addsuffix .lo,$(DLL_PRELOAD))
@ -433,6 +438,9 @@ libsane-net.la: ../sanei/sanei_wire.lo
libsane-niash.la: ../sanei/sanei_constrain_value.lo
libsane-niash.la: ../sanei/sanei_usb.lo
libsane-pint.la: ../sanei/sanei_constrain_value.lo
libsane-pixma.la: ../sanei/sanei_usb.lo
libsane-pixma.la: ../sanei/sanei_thread.lo
libsane-pixma.la: $(addsuffix .lo,$(EXTRA_pixma))
libsane-plustek.la: ../sanei/sanei_constrain_value.lo
libsane-plustek.la: ../sanei/sanei_usb.lo
libsane-plustek.la: ../sanei/sanei_lm983x.lo

Wyświetl plik

@ -43,6 +43,7 @@ nec
niash
pie
pint
pixma
plustek
#plustek_pp
#pnm

1443
backend/pixma.c 100644

Plik diff jest za duży Load Diff

340
backend/pixma.h 100644
Wyświetl plik

@ -0,0 +1,340 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#ifndef PIXMA_H
#define PIXMA_H
/*! \mainpage Scanner driver for Canon PIXMA MP series
\section example Sample code for application
\code
pixma_set_debug_level(level);
pixma_init();
nscanners = pixma_find_scanners();
devnr = choose_scanner(nscanners);
scanner = pixma_open(devnr);
setup_param(param);
pixma_check_scan_param(scanner, param);
do {
if (I_need_events &&
(ev = pixma_wait_event(scanner, timeout)) > 0) {
handle_event(ev);
}
pixma_scan(scanner, param);
while ((count = pixma_read_image(scanner, buf, len)) > 0) {
write(buf, count);
if (error_occured_in_write) {
pixma_cancel(scanner);
}
}
} while (!enough);
pixma_close(scanner);
pixma_cleanup();
\endcode
<b>Note:</b> pixma_cancel() can be called asynchronously to
interrupt pixma_read_image(). It does not cancel the operation
immediately. pixma_read_image() <em>must</em> be called until it
returns zero or an error (probably \c -ECANCELED).
\section reference Reference
- \subpage API
- \subpage IO
- \subpage subdriver
- \subpage debug
*/
/*!
* \defgroup API The driver API
* \brief The driver API.
*
* The return value of functions that returns \c int has the following
* meaning if not otherwise specified:
* - >= 0 if succeeded
* - < 0 if failed
* - \c -EBUSY
* - \c -ECANCELED
* - \c -EINVAL
* - \c -ENOMEM
* - \c -EPROTO unexpected reply from scanner.
* - \c -ETIMEDOUT
* - \c -EDEADLK papar jam
* - \c -ENODATA no paper
* - \c -ENOLCK scanner cover is opened
*/
#ifdef HAVE_STDINT_H
# include <stdint.h> /* available in ISO C99 */
#else
# include <sys/types.h>
typedef u_int8_t uint8_t;
typedef u_int16_t uint16_t;
typedef u_int32_t uint32_t;
#endif /* HAVE_STDINT_H */
/** \addtogroup API
* @{ */
/** \name Version of the driver */
/**@{*/
#define PIXMA_VERSION_MAJOR 0
#define PIXMA_VERSION_MINOR 11
#define PIXMA_VERSION_BUILD 2
/**@}*/
/** \name Capabilities for using with pixma_config_t::cap */
/**@{*/
#define PIXMA_CAP_EASY_RGB (1 << 0)
#define PIXMA_CAP_GRAY (1 << 1)
#define PIXMA_CAP_ADF (1 << 2)
#define PIXMA_CAP_16BIT (1 << 3)
#define PIXMA_CAP_GAMMA_TABLE (1 << 4)
#define PIXMA_CAP_EVENTS (1 << 5)
#define PIXMA_CAP_EXPERIMENT (1 << 31)
/**@}*/
/** \name Button events returned by pixma_wait_event() */
/**@{*/
#define PIXMA_EV_NONE 0
#define PIXMA_EV_ACTION_MASK (0xff)
#define PIXMA_EV_BUTTON1 (1 << 8)
#define PIXMA_EV_BUTTON2 (2 << 8)
/**@}*/
/** @} end of API group */
struct pixma_t;
struct pixma_scan_ops_t;
struct pixma_scan_param_t;
struct pixma_config_t;
struct pixma_cmdbuf_t;
struct pixma_imagebuf_t;
typedef struct pixma_t pixma_t;
typedef struct pixma_scan_ops_t pixma_scan_ops_t;
typedef struct pixma_scan_param_t pixma_scan_param_t;
typedef struct pixma_config_t pixma_config_t;
typedef struct pixma_cmdbuf_t pixma_cmdbuf_t;
typedef struct pixma_imagebuf_t pixma_imagebuf_t;
/** \addtogroup API
* @{ */
/** String index constants */
typedef enum pixma_string_index_t
{
PIXMA_STRING_MODEL,
PIXMA_STRING_ID,
PIXMA_STRING_LAST
} pixma_string_index_t;
/** Paper sources */
typedef enum pixma_paper_source_t
{
PIXMA_SOURCE_FLATBED,
PIXMA_SOURCE_ADF
} pixma_paper_source_t;
/** Scan parameters. */
struct pixma_scan_param_t
{
/** Size in bytes of one image line (row).
* line_size >= depth / 8 * channels * w <br>
* This field will be set by pixma_check_scan_param(). */
unsigned line_size;
/** Size in bytes of the whole image.
* image_size = line_size * h <br>
* This field will be set by pixma_check_scan_param(). */
unsigned image_size;
/** Channels per pixel. 1 = grayscale, 3 = color */
unsigned channels;
/** Bits per channels. 0 = default. Currently not used. */
unsigned depth;
/*@{ */
/** Resolution. Valid values are 75,150,300,600,1200... */
unsigned xdpi, ydpi;
/*@} */
/*! \name Scan area in pixels
* (0,0) = top left; positive x extends to the right; positive y to the
* bottom; in pixels. */
/*@{ */
unsigned x, y, w, h;
/*@} */
/** Gamma table. 4096 entries, 12 bit => 8 bit. If \c NULL, default gamma
* specified by subdriver will be used. */
const uint8_t *gamma_table;
/** \see #pixma_paper_source_t */
pixma_paper_source_t source;
};
/** PIXMA model information */
struct pixma_config_t
{
/* If you change this structure, don't forget to update the device list in
* subdrivers. */
const char *name; /**< Model name. */
uint16_t vid; /**< USB Vendor ID */
uint16_t pid; /**< USB Product ID */
unsigned iface; /**< USB Interface number */
const pixma_scan_ops_t *ops; /**< Subdriver ops */
unsigned xdpi; /**< Maximum horizontal resolution[DPI] */
unsigned ydpi; /**< Maximum vertical resolution[DPI] */
unsigned width; /**< Maximum width of scannable area in pixels at 75DPI */
unsigned height; /**< Maximum height of scannable area in pixels at 75DPI */
unsigned cap; /**< Capability bitfield \see PIXMA_CAP_* */
};
/* Defined in pixma_common.c */
/** Initialize the driver. It must be called before any other functions
* except pixma_set_debug_level(). */
int pixma_init (void);
/** Free resources allocated by the driver. */
void pixma_cleanup (void);
/** Set the debug level.
* \param[in] level the debug level
* - 0 No debug output at all
* - 1 Only errors and warning
* - 2 General information
* - 3 Debugging messages
* - 10 USB traffic dump */
void pixma_set_debug_level (int level);
/** Find scanners. The device number used in pixma_open(),
* pixma_get_device_model(), pixma_get_device_id() and
* pixma_get_device_config() must be less than the value returned by the last
* call of this function.
*
* \return The number of scanners found currently. The return value is
* guaranteed to be valid until the next call to pixma_find_scanners(). */
int pixma_find_scanners (void);
/** Return the model name of the device \a devnr. */
const char *pixma_get_device_model (unsigned devnr);
/** Return the unique ID of the device \a devnr. */
const char *pixma_get_device_id (unsigned devnr);
/** Return the device configuration of the device \a devnr. */
const struct pixma_config_t *pixma_get_device_config (unsigned devnr);
/** Open a connection to the scanner \a devnr.
* \param[in] devnr The scanner number
* \param[out] handle The device handle
* \see pixma_find_scanners() */
int pixma_open (unsigned devnr, pixma_t ** handle);
/** Close the connection to the scanner. The scanning process is aborted
* if necessary before the function returns. */
void pixma_close (pixma_t * s);
/** Initiate an image acquisition process. You must keep \a sp valid until the
* image acquisition process has finished. */
int pixma_scan (pixma_t *, pixma_scan_param_t * sp);
/** Read a block of image data. It blocks until there is at least one byte
* available or an error occurs.
*
* \param[out] buf Pointer to the buffer
* \param[in] len Size of the buffer
*
* \retval count Number of bytes written to the buffer or error. Possible
* return value:
* - count = 0 for end of image
* - count = \a len
* - 0 < count < \a len if and only if it is the last block.
* - count < 0 for error */
int pixma_read_image (pixma_t *, void *buf, unsigned len);
#if 0
/** Read a block of image data and write to \a fd.
* \param[in] fd output file descriptor
* \see pixma_read_image() */
int pixma_read_image_write (pixma_t *, int fd);
#endif
/** Cancel the scanning process. No effect if no scanning process is in
* progress. It can be called asynchronously e.g. within a signal
* handle. pixma_cancel() doesn't abort the operation immediately. It
* guarantees that the current call or, at the latest, the next call to
* pixma_read_image() will return zero or an error (probably -ECANCELED). */
void pixma_cancel (pixma_t *);
/** Check the scan parameters. This function can change your parameters to
* match the device capability, e.g. adjust width and height to the available
* area.
* \return -EINVAL for invalid parameters. */
int pixma_check_scan_param (pixma_t *, pixma_scan_param_t *);
/** Wait until a scanner button is pressed or it times out. It should not be
* called during image acquisition is in progress.
* \param[in] timeout in milliseconds, less than 0 means forever
* \return
* - \c PIXMA_EV_NONE if it timed out.
* - non-zero value indicates which button was pressed.
* \see PIXMA_EV_*
*/
uint32_t pixma_wait_event (pixma_t *, int timeout);
/** Enable or disable background tasks. Currently, the only one task
* is submitting interrupt URB in background.
* \param[in] enabled if not zero, enable background task.
* \see pixma_set_interrupt_mode() */
int pixma_enable_background (pixma_t *, int enabled);
const char *pixma_get_string (pixma_t *, pixma_string_index_t);
const pixma_config_t *pixma_get_config (pixma_t *);
void pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n);
/** @} end of API group */
#endif

Wyświetl plik

@ -0,0 +1,712 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
# include "../include/sane/config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h> /* pow(C90) */
#include <errno.h> /* POSIX */
#include <sys/time.h> /* gettimeofday(4.3BSD) */
#include <unistd.h> /* usleep */
#include "pixma_rename.h"
#include "pixma_common.h"
#include "pixma_io.h"
#ifdef __GNUC__
# define UNUSED(v) (void) v
#else
# define UNUSED(v)
#endif
extern const pixma_config_t pixma_mp150_devices[];
extern const pixma_config_t pixma_mp750_devices[];
extern const pixma_config_t pixma_mp730_devices[];
static const pixma_config_t *const pixma_devices[] = {
pixma_mp150_devices,
pixma_mp750_devices,
pixma_mp730_devices,
NULL
};
static pixma_t *first_pixma = NULL;
static time_t tstart_sec = 0;
static uint32_t tstart_usec = 0;
static int debug_level = 1;
#ifndef NDEBUG
static void
u8tohex (uint8_t x, char *str)
{
static const char hdigit[16] =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
str[0] = hdigit[(x >> 4) & 0xf];
str[1] = hdigit[x & 0xf];
str[2] = '\0';
}
static void
u32tohex (uint32_t x, char *str)
{
u8tohex (x >> 24, str);
u8tohex (x >> 16, str + 2);
u8tohex (x >> 8, str + 4);
u8tohex (x, str + 6);
}
void
pixma_hexdump (int level, const void *d_, unsigned len)
{
const uint8_t *d = (const uint8_t *) (d_);
unsigned ofs, c;
char line[100]; /* actually only 1+8+1+8*3+1+8*3+1 = 61 bytes needed */
if (level > debug_level)
return;
ofs = 0;
while (ofs < len)
{
char *p;
line[0] = ' ';
u32tohex (ofs, line + 1);
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++;
}
}
p[0] = '\0';
pixma_dbg (level, "%s\n", line);
ofs += c;
}
}
static int
time2str (char *buf, unsigned size)
{
time_t sec;
uint32_t usec;
pixma_get_time (&sec, &usec);
sec -= tstart_sec;
if (usec >= tstart_usec)
{
usec -= tstart_usec;
}
else
{
usec = 1000000 + usec - tstart_usec;
sec--;
}
return snprintf (buf, size, "%lu.%03u", (unsigned long) sec,
(unsigned) (usec / 1000));
}
void
pixma_dump (int level, const char *type, const void *data, int len,
int size, int max)
{
int actual_len, print_len;
char buf[20];
if (level > debug_level)
return;
if (debug_level >= 20)
max = -1; /* dump every bytes */
time2str (buf, sizeof (buf));
pixma_dbg (level, "%s T=%s len=%d\n", type, buf, len);
actual_len = (size >= 0) ? size : len;
print_len = (max >= 0 && max < actual_len) ? max : actual_len;
if (print_len >= 0)
{
pixma_hexdump (level, data, print_len);
if (print_len < actual_len)
pixma_dbg (level, " ...\n");
}
if (len < 0)
pixma_dbg (level, " ERROR: %s\n", strerror (-len));
pixma_dbg (level, "\n");
}
#endif /* NDEBUG */
void
pixma_set_debug_level (int level)
{
debug_level = level;
}
void
pixma_set_be16 (uint16_t x, uint8_t * buf)
{
buf[0] = x >> 8;
buf[1] = x;
}
void
pixma_set_be32 (uint32_t x, uint8_t * buf)
{
buf[0] = x >> 24;
buf[1] = x >> 16;
buf[2] = x >> 8;
buf[3] = x;
}
uint16_t
pixma_get_be16 (const uint8_t * buf)
{
return ((uint16_t) buf[0] << 8) | buf[1];
}
uint32_t
pixma_get_be32 (const uint8_t * buf)
{
return ((uint32_t) buf[0] << 24) + ((uint32_t) buf[1] << 16) +
((uint32_t) buf[2] << 8) + buf[3];
}
uint8_t
pixma_sum_bytes (const void *data, unsigned len)
{
const uint8_t *d = (const uint8_t *) data;
unsigned i, sum = 0;
for (i = 0; i != len; i++)
sum += d[i];
return sum;
}
void
pixma_sleep (unsigned long usec)
{
usleep (usec);
}
void
pixma_get_time (time_t * sec, uint32_t * usec)
{
struct timeval tv;
gettimeofday (&tv, NULL);
if (sec)
*sec = tv.tv_sec;
if (usec)
*usec = tv.tv_usec;
}
int
pixma_map_status_errno (unsigned status)
{
switch (status)
{
case PIXMA_STATUS_OK:
return 0;
case PIXMA_STATUS_FAILED:
return -ECANCELED;
case PIXMA_STATUS_BUSY:
return -EBUSY;
default:
return -EPROTO;
}
}
int
pixma_check_result (pixma_cmdbuf_t * cb)
{
const uint8_t *r = cb->buf;
unsigned header_len = cb->res_header_len;
unsigned expected_reslen = cb->expected_reslen;
int error;
unsigned len;
if (cb->reslen < 0)
return cb->reslen;
len = (unsigned) cb->reslen;
if (len >= header_len)
{
error = pixma_map_status_errno (pixma_get_be16 (r));
if (expected_reslen != 0)
{
if (len == expected_reslen)
{
if (pixma_sum_bytes (r + header_len, len - header_len) != 0)
error = -EPROTO;
}
else
{
/* This case will happen when a command cannot be completely
executed, e.g. because you press the cancel button. The
device will return only a header with PIXMA_STATUS_FAILED. */
if (len != header_len)
error = -EPROTO;
}
}
}
else
error = -EPROTO;
#ifndef NDEBUG
if (error == -EPROTO)
{
pixma_dbg (1, "WARNING: result len=%d expected %d: %s\n",
len, cb->expected_reslen, strerror (-error));
pixma_hexdump (1, r, MIN (len, 64));
}
#endif
return error;
}
int
pixma_cmd_transaction (pixma_t * s, const void *cmd, unsigned cmdlen,
void *data, unsigned expected_len)
{
int error, tmo;
error = pixma_write (s->io, cmd, cmdlen);
if (error != (int) cmdlen)
return error; /* FIXME: make sure that error < 0! */
/* When you send the start_session command while the scanner optic is
going back to the home position after the last scan session has been
cancelled, you won't get the response before it arrives home. This takes
about 5 seconds. If the last session was succeeded, the scanner will
immediatly answer with PIXMA_STATUS_BUSY.
Is 8 seconds timeout enough? This affects ALL commands that use
pixma_cmd_transaction(). */
tmo = 8;
do
{
error = pixma_read (s->io, data, expected_len);
if (error == -ETIMEDOUT)
PDBG (pixma_dbg (2, "No response yet. Timed out in %d sec.\n", tmo));
}
while (error == -ETIMEDOUT && --tmo != 0);
if (error < 0)
{
PDBG (pixma_dbg (1, "WARNING:Error in response phase. cmd:%02x%02x\n",
((const uint8_t *) cmd)[0],
((const uint8_t *) cmd)[1]));
PDBG (pixma_dbg
(1,
" If the scanner hangs, reset it and/or unplug the "
"USB cable.\n"));
}
return error; /* length of the result packet or error */
}
uint8_t *
pixma_newcmd (pixma_cmdbuf_t * cb, unsigned cmd,
unsigned dataout, unsigned datain)
{
unsigned cmdlen = cb->cmd_header_len + dataout;
unsigned reslen = cb->res_header_len + datain;
if (cmdlen > cb->size || reslen > cb->size)
return NULL;
memset (cb->buf, 0, cmdlen);
cb->cmdlen = cmdlen;
cb->expected_reslen = reslen;
pixma_set_be16 (cmd, cb->buf);
pixma_set_be16 (dataout + datain, cb->buf + cb->cmd_len_field_ofs);
if (dataout != 0)
return cb->buf + cb->cmd_header_len;
else
return cb->buf + cb->res_header_len;
}
int
pixma_exec (pixma_t * s, pixma_cmdbuf_t * cb)
{
if (cb->cmdlen > cb->cmd_header_len)
pixma_fill_checksum (cb->buf + cb->cmd_header_len,
cb->buf + cb->cmdlen - 1);
cb->reslen =
pixma_cmd_transaction (s, cb->buf, cb->cmdlen, cb->buf,
cb->expected_reslen);
return pixma_check_result (cb);
}
int
pixma_exec_short_cmd (pixma_t * s, pixma_cmdbuf_t * cb, unsigned cmd)
{
pixma_newcmd (cb, cmd, 0, 0);
return pixma_exec (s, cb);
}
int
pixma_check_dpi (unsigned dpi, unsigned max)
{
/* valid dpi = 75 * 2^n */
unsigned temp = dpi / 75;
if (dpi > max || dpi < 75 || 75 * temp != dpi || (temp & (temp - 1)) != 0)
return -EINVAL;
return 0;
}
int
pixma_init (void)
{
PDBG (pixma_dbg (2, "pixma version %d.%d.%d\n", PIXMA_VERSION_MAJOR,
PIXMA_VERSION_MINOR, PIXMA_VERSION_BUILD));
PASSERT (first_pixma == NULL);
if (tstart_sec == 0)
pixma_get_time (&tstart_sec, &tstart_usec);
return pixma_io_init ();
}
void
pixma_cleanup (void)
{
while (first_pixma)
pixma_close (first_pixma);
pixma_io_cleanup ();
}
int
pixma_open (unsigned devnr, pixma_t ** handle)
{
int error;
pixma_t *s;
const pixma_config_t *cfg;
*handle = NULL;
cfg = pixma_get_device_config (devnr);
if (!cfg)
return -EINVAL; /* invalid devnr */
PDBG (pixma_dbg (2, "pixma_open(): %s\n", cfg->name));
s = (pixma_t *) calloc (1, sizeof (s[0]));
if (!s)
return -ENOMEM;
s->next = first_pixma;
first_pixma = s;
s->cfg = cfg;
error = pixma_connect (devnr, &s->io);
if (error < 0)
{
PDBG (pixma_dbg (2, "pixma_connect() failed:%s\n", strerror (-error)));
goto rollback;
}
strncpy (s->id, pixma_get_device_id (devnr), sizeof (s->id));
s->ops = s->cfg->ops;
s->scanning = 0;
error = s->ops->open (s);
if (error < 0)
goto rollback;
*handle = s;
return 0;
rollback:
pixma_close (s);
return error;
}
void
pixma_close (pixma_t * s)
{
pixma_t **p;
if (!s)
return;
for (p = &first_pixma; *p && *p != s; p = &((*p)->next))
{
}
PASSERT (*p);
if (!(*p))
return;
PDBG (pixma_dbg (2, "pixma_close(): %s\n", s->cfg->name));
if (s->io)
{
if (s->scanning)
{
PDBG (pixma_dbg (3, "pixma_close():scanning in progress, call"
" finish_scan()\n"));
s->ops->finish_scan (s);
}
s->ops->close (s);
pixma_disconnect (s->io);
}
*p = s->next;
free (s);
}
int
pixma_scan (pixma_t * s, pixma_scan_param_t * sp)
{
int error;
error = pixma_check_scan_param (s, sp);
if (error < 0)
return error;
#ifndef NDEBUG
pixma_dbg (3, "\n");
pixma_dbg (3, "pixma_scan(): start\n");
pixma_dbg (3, " line_size=%u image_size=%u channels=%u depth=%u\n",
sp->line_size, sp->image_size, sp->channels, sp->depth);
pixma_dbg (3, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n",
sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h);
pixma_dbg (3, " gamma_table=%p source=%d\n", sp->gamma_table, sp->source);
#endif
s->param = sp;
s->cancel = 0;
s->cur_image_size = 0;
s->imagebuf.wptr = NULL;
s->imagebuf.wend = NULL;
s->imagebuf.rptr = NULL;
s->imagebuf.rend = NULL;
error = s->ops->scan (s);
if (error >= 0)
{
s->scanning = 1;
}
else
{
PDBG (pixma_dbg (3, "pixma_scan() failed:%s\n", strerror (-error)));
}
return error;
}
int
pixma_read_image (pixma_t * s, void *buf, unsigned len)
{
int result;
pixma_imagebuf_t ib;
if (!s->scanning)
return 0;
if (s->cancel)
{
result = -ECANCELED;
goto cancel;
}
ib = s->imagebuf; /* get rptr and rend */
ib.wptr = (uint8_t *) buf;
ib.wend = ib.wptr + len;
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);
s->scanning = 0;
if (s->cur_image_size != s->param->image_size)
{
PDBG (pixma_dbg (1, "WARNING:image size mismatches: "
"%u expected, %u received\n",
s->param->image_size, s->cur_image_size));
}
PDBG (pixma_dbg (3, "pixma_read_image():completed\n"));
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;
}
}
s->imagebuf = ib;
return ib.wptr - (uint8_t *) buf;
cancel:
s->ops->finish_scan (s);
s->scanning = 0;
if (result == -ECANCELED)
{
PDBG (pixma_dbg (3, "pixma_read_image():cancelled by %sware\n",
(s->cancel) ? "soft" : "hard"));
}
return result;
}
void
pixma_cancel (pixma_t * s)
{
s->cancel = 1;
}
int
pixma_enable_background (pixma_t * s, int enabled)
{
return pixma_set_interrupt_mode (s->io, enabled);
}
unsigned
pixma_wait_event (pixma_t * s, int timeout /*ms */ )
{
unsigned events;
if (s->events == PIXMA_EV_NONE && s->ops->wait_event)
s->ops->wait_event (s, timeout);
events = s->events;
s->events = 0;
return events;
}
#define CLAMP2(x,w,min,max,dpi) do { \
unsigned m = (max) * (dpi) / 75; \
x = MIN(x, m - min); \
w = MIN(w, m - x); \
if (w < min) w = min; \
} while(0)
int
pixma_check_scan_param (pixma_t * s, pixma_scan_param_t * sp)
{
if (!(sp->channels == 3 ||
(sp->channels == 1 && (s->cfg->cap & PIXMA_CAP_GRAY) != 0)))
return -EINVAL;
if (pixma_check_dpi (sp->xdpi, s->cfg->xdpi) < 0 ||
pixma_check_dpi (sp->ydpi, s->cfg->ydpi) < 0)
return -EINVAL;
/* xdpi must be equal to ydpi except that
xdpi = max_xdpi and ydpi = max_ydpi. */
if (!(sp->xdpi == sp->ydpi ||
(sp->xdpi == s->cfg->xdpi && sp->ydpi == s->cfg->ydpi)))
return -EINVAL;
/* FIXME: I assume the same minimum width and height for every model. */
CLAMP2 (sp->x, sp->w, 13, s->cfg->width, sp->xdpi);
CLAMP2 (sp->y, sp->h, 1, s->cfg->height, sp->ydpi);
if (!(s->cfg->cap & PIXMA_CAP_ADF))
sp->source = PIXMA_SOURCE_FLATBED;
if (sp->depth == 0)
sp->depth = 8;
if ((sp->depth % 8) != 0)
return -EINVAL;
sp->line_size = 0;
if (s->ops->check_param (s, sp) < 0)
return -EINVAL;
if (sp->line_size == 0)
sp->line_size = sp->depth / 8 * sp->channels * sp->w;
sp->image_size = sp->line_size * sp->h;
return 0;
}
const char *
pixma_get_string (pixma_t * s, pixma_string_index_t i)
{
switch (i)
{
case PIXMA_STRING_MODEL:
return s->cfg->name;
case PIXMA_STRING_ID:
return s->id;
case PIXMA_STRING_LAST:
return NULL;
}
return NULL;
}
const pixma_config_t *
pixma_get_config (pixma_t * s)
{
return s->cfg;
}
void
pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n)
{
int i;
double r_gamma = 1.0 / gamma;
double out_scale = 255.0;
double in_scale = 1.0 / (n - 1);
for (i = 0; (unsigned) i != n; i++)
{
table[i] = (int) (out_scale * pow (i * in_scale, r_gamma) + 0.5);
}
}
int
pixma_find_scanners (void)
{
return pixma_collect_devices (pixma_devices);
}
const char *
pixma_get_device_model (unsigned devnr)
{
const pixma_config_t *cfg = pixma_get_device_config (devnr);
return (cfg) ? cfg->name : NULL;
}

Wyświetl plik

@ -0,0 +1,217 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#ifndef PIXMA_COMMON_H
#define PIXMA_COMMON_H
#include <time.h> /* time_t */
#include "pixma.h"
/*! \defgroup subdriver Subdriver Interface
* \brief Subdriver interface. */
/*! \defgroup debug Debug utilities
* \brief Debug utilities. */
#ifdef NDEBUG
# define PDBG(x) do {} while(0)
# define PASSERT(x) do {} while(0)
#else
# define PDBG(x) x
# define PASSERT(x) do { \
if (!(x)) \
pixma_dbg(1, "ASSERT failed:%s:%d: " \
#x "\n", __FILE__, __LINE__); \
} while(0)
#endif
#define PIXMA_STATUS_OK 0x0606
#define PIXMA_STATUS_FAILED 0x1515
#define PIXMA_STATUS_BUSY 0x1414
#define PIXMA_MAX_ID_LEN 30
#define MIN(x,y) (((x) < (y)) ? (x):(y))
#define MAX(x,y) (((x) < (y)) ? (y):(x))
#define ALIGN(x,n) (((x) + (n) - 1) / (n) * (n))
struct pixma_io_t;
struct pixma_limits_t
{
unsigned xdpi, ydpi;
unsigned width, height;
};
struct pixma_cmdbuf_t
{
unsigned cmd_header_len, res_header_len, cmd_len_field_ofs;
unsigned expected_reslen, cmdlen;
int reslen;
unsigned size;
uint8_t *buf;
};
struct pixma_imagebuf_t
{
uint8_t *wptr, *wend;
const uint8_t *rptr, *rend;
};
struct pixma_t
{
pixma_t *next;
struct pixma_io_t *io;
const pixma_scan_ops_t *ops;
pixma_scan_param_t *param;
const pixma_config_t *cfg;
char id[PIXMA_MAX_ID_LEN + 1];
int cancel;
uint32_t events;
void *subdriver; /* can be used by model driver. */
/* private */
unsigned scanning:1;
unsigned cur_image_size;
pixma_imagebuf_t imagebuf;
};
/** \addtogroup subdriver
* @{ */
/** Scan operations for subdriver. */
struct pixma_scan_ops_t
{
/** Allocate a data structure for the subdriver. It is called after the
* core driver connected to the scanner. The subdriver should reset the
* scanner to a known state in this function. */
int (*open) (pixma_t *);
/** Free resources allocated by the subdriver. Don't forget to send abort
* command to the scanner if it is scanning. */
void (*close) (pixma_t *);
/** Setup the scanner for scan parameters defined in \a s->param. */
int (*scan) (pixma_t * s);
/** Fill a buffer with image data. The subdriver has two choices:
* -# Fill the buffer pointed by ib->wptr directly and leave
* ib->rptr and ib->rend untouched. The length of the buffer is
* ib->wend - ib->wptr. It must update ib->wptr accordingly.
* -# Update ib->rptr and ib->rend to point to the the beginning and
* the end of the internal buffer resp. The length of the buffer
* is ib->rend - ib->rptr. This function is called again if
* and only if pixma_read_image() has copied the whole buffer.
*
* The subdriver must wait until there is at least one byte to read or
* return 0 for the end of image. */
int (*fill_buffer) (pixma_t *, pixma_imagebuf_t * ib);
/** Cancel the scan operation if necessary and free resources allocated in
* scan(). */
void (*finish_scan) (pixma_t *);
/** [Optional] Wait for a user's event, e.g. button event. \a timeout is
* in milliseconds. If an event occured before it's timed out, flags in
* \a s->events should be set accordingly.
* \see PIXMA_EV_* */
void (*wait_event) (pixma_t * s, int timeout);
/** Check the scan parameters. The parameters can be adjusted if they are
* out of range, e.g. width > max_width. */
int (*check_param) (pixma_t *, pixma_scan_param_t *);
};
/** \name Funtions for read and write big-endian integer values */
/**@{*/
void pixma_set_be16 (uint16_t x, uint8_t * buf);
void pixma_set_be32 (uint32_t x, uint8_t * buf);
uint16_t pixma_get_be16 (const uint8_t * buf);
uint32_t pixma_get_be32 (const uint8_t * buf);
/**@}*/
/** \name Utility functions */
/**@{*/
uint8_t pixma_sum_bytes (const void *data, unsigned len);
int pixma_check_dpi (unsigned dpi, unsigned max);
void pixma_sleep (unsigned long usec);
void pixma_get_time (time_t * sec, uint32_t * usec);
/**@}*/
/** \name Command related functions */
/**@{*/
int pixma_cmd_transaction (pixma_t *, const void *cmd, unsigned cmdlen,
void *data, unsigned expected_len);
int pixma_check_result (pixma_cmdbuf_t *);
uint8_t *pixma_newcmd (pixma_cmdbuf_t *, unsigned cmd,
unsigned dataout, unsigned datain);
int pixma_exec (pixma_t *, pixma_cmdbuf_t *);
int pixma_exec_short_cmd (pixma_t *, pixma_cmdbuf_t *, unsigned cmd);
int pixma_map_status_errno (unsigned status);
/**@}*/
#define pixma_fill_checksum(start, end) do { \
*(end) = -pixma_sum_bytes(start, (end)-(start)); \
} while(0)
/** @} end of group subdriver */
/** \addtogroup debug
* @{ */
void pixma_set_debug_level (int level);
#ifndef NDEBUG
void pixma_hexdump (int level, const void *d_, unsigned len);
/* len: length of data or error code.
size: if >= 0, force to print 'size' bytes.
max: maximum number of bytes to print(-1 means no limit). */
void pixma_dump (int level, const char *type, const void *data, int len,
int size, int max);
# define DEBUG_DECLARE_ONLY
# include "../include/sane/sanei_debug.h"
#endif /* NDEBUG */
/** @} end of group debug */
#endif

143
backend/pixma_io.h 100644
Wyświetl plik

@ -0,0 +1,143 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#ifndef PIXMA_IO_H
#define PIXMA_IO_H
/* TODO: move to pixma_common.h, to reduce the number of files */
/*!
* \defgroup IO IO interface
* \brief The IO interface.
*
* Return value of functions that return \c int if not otherwise specified:
* - < 0 if failed (e.g. \c -ETIMEDOUT)
* - >= if succeeded
* @{
*/
/** Timeout for pixma_read() in milliseconds */
#define PIXMA_BULKIN_TIMEOUT 1000
/** Timeout for pixma_write() in milliseconds */
#define PIXMA_BULKOUT_TIMEOUT 1000
struct pixma_io_t;
struct pixma_config_t;
/** IO handle */
typedef struct pixma_io_t pixma_io_t;
/** Initialize IO module. It must be called before any other functions in this
* module. */
int pixma_io_init (void);
/** Shutdown all connections and free resources allocated in this module. */
void pixma_io_cleanup (void);
/** Find devices currently connected to the computer.
* \c devnr passed to functions
* - pixma_get_device_config()
* - pixma_get_device_id()
* - pixma_connect()
* .
* should be less than the number of devices returned by this function.
* \param[in] pixma_devices A \c NULL terminated array of pointers to
* array of pixma_config_t which is terminated by setting
* pixma_config_t::name to \c NULL.
* \return Number of devices found */
int pixma_collect_devices (const struct pixma_config_t *const
pixma_devices[]);
/** Get device configuration. */
const struct pixma_config_t *pixma_get_device_config (unsigned devnr);
/** Get a unique ID of the device \a devnr. */
const char *pixma_get_device_id (unsigned devnr);
/** Connect to the device and claim the scanner interface.
* \param[in] devnr
* \param[out] handle */
int pixma_connect (unsigned devnr, pixma_io_t ** handle);
/** Release the scanner interface and disconnect from the device. */
void pixma_disconnect (pixma_io_t *);
/** Reset the USB interface. \warning Use with care! */
int pixma_reset_device (pixma_io_t *);
/** Write data to the device.
* \return number of bytes successfully written, or < 0 if failed.
* \param[in] cmd Data
* \param[in] len Length of data
* \see #PIXMA_BULKOUT_TIMEOUT */
int pixma_write (pixma_io_t *, const void *cmd, unsigned len);
/** Read data from the device.
* \return
* - Number of bytes successfully read. A return value of zero means that
* a zero length USB packet was received.
* - < 0 if failed
* \param[out] buf
* \param[in] size of the buffer
* \see #PIXMA_BULKIN_TIMEOUT */
int pixma_read (pixma_io_t *, void *buf, unsigned size);
/** Wait for an interrupt. This function can be interrupted by a signal.
* \param[out] buf
* \param[in] size of the buffer
* \param[in] timeout in milliseconds; if < 0, wait forever.
* \return size of the received packet or error code (\c -EINTR if
* interrupted by a signal). */
int pixma_wait_interrupt (pixma_io_t *, void *buf, unsigned size,
int timeout);
/** Enable or disable background interrupt monitoring.
* Background mode is enabled by default.
* \param[in] background if not zero, a URB is submitted in background
* for interrupt endpoint. */
int pixma_set_interrupt_mode (pixma_io_t *, int background);
/** @} end of IO group */
#endif

Wyświetl plik

@ -0,0 +1,418 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#include "../include/sane/config.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h> /* INT_MAX */
#include "pixma_rename.h"
#include "pixma_common.h"
#include "pixma_io.h"
#include "../include/sane/sanei_usb.h"
#ifdef __GNUC__
# define UNUSED(v) (void) v
#else
# define UNUSED(v)
#endif
struct pixma_io_t
{
pixma_io_t *next;
SANE_Int usb;
};
typedef struct scanner_info_t
{
struct scanner_info_t *next;
char *devname;
const pixma_config_t *cfg;
char serial[PIXMA_MAX_ID_LEN + 1]; /* "xxxxyyyy_zzzzzzz..."
x = vid, y = pid, z = serial */
} scanner_info_t;
static scanner_info_t *first_scanner = NULL;
static pixma_io_t *first_io = NULL;
static unsigned nscanners;
static scanner_info_t *
get_scanner_info (unsigned devnr)
{
scanner_info_t *si;
for (si = first_scanner; si && devnr != 0; --devnr, si = si->next)
{
}
return si;
}
static SANE_Status
attach (SANE_String_Const devname)
{
scanner_info_t *si;
si = (scanner_info_t *) calloc (1, sizeof (*si));
if (!si)
return SANE_STATUS_NO_MEM;
si->devname = strdup (devname);
if (!si->devname)
return SANE_STATUS_NO_MEM;
si->next = first_scanner;
first_scanner = si;
nscanners++;
return SANE_STATUS_GOOD;
}
static void
clear_scanner_list (void)
{
scanner_info_t *si = first_scanner;
while (si)
{
scanner_info_t *temp = si;
free (si->devname);
si = si->next;
free (temp);
}
nscanners = 0;
first_scanner = NULL;
}
static SANE_Status
get_descriptor (SANE_Int dn, SANE_Int type, SANE_Int descidx,
SANE_Int index, SANE_Int length, SANE_Byte * data)
{
return sanei_usb_control_msg (dn, 0x80, USB_REQ_GET_DESCRIPTOR,
((type & 0xff) << 8) | (descidx & 0xff),
index, length, data);
}
static SANE_Status
get_string_descriptor (SANE_Int dn, SANE_Int index, SANE_Int lang,
SANE_Int length, SANE_Byte * data)
{
return get_descriptor (dn, USB_DT_STRING, index, lang, length, data);
}
static void
u16tohex (uint16_t x, char *str)
{
static const char hdigit[16] =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F' };
str[0] = hdigit[(x >> 12) & 0xf];
str[1] = hdigit[(x >> 8) & 0xf];
str[2] = hdigit[(x >> 4) & 0xf];
str[3] = hdigit[x & 0xf];
str[4] = '\0';
}
static void
read_serial_number (scanner_info_t * si)
{
uint8_t unicode[2 * (PIXMA_MAX_ID_LEN - 9) + 2];
uint8_t ddesc[18];
int i, len, iSerialNumber;
SANE_Int usb;
SANE_Status status;
char *serial = si->serial;
u16tohex (si->cfg->vid, serial);
u16tohex (si->cfg->pid, serial + 4);
if (SANE_STATUS_GOOD != sanei_usb_open (si->devname, &usb))
return;
if (get_descriptor (usb, USB_DT_DEVICE, 0, 0, 18, ddesc)
!= SANE_STATUS_GOOD)
goto done;
iSerialNumber = ddesc[16];
if (iSerialNumber != 0)
{
int iSerialNumber = ddesc[16];
if (get_string_descriptor (usb, 0, 0, 4, unicode) != SANE_STATUS_GOOD)
goto done;
status =
get_string_descriptor (usb, iSerialNumber,
unicode[3] * 256 + unicode[2],
sizeof (unicode), unicode);
if (status != SANE_STATUS_GOOD)
goto done;
/* Assumed charset: Latin1 */
len = unicode[0];
if (len > (int) sizeof (unicode))
{
len = sizeof (unicode);
PDBG (pixma_dbg (1, "WARNING:Truncated serial number\n"));
}
serial[8] = '_';
for (i = 2; i < len; i += 2)
{
serial[9 + i / 2 - 1] = unicode[i];
}
serial[9 + i / 2 - 1] = '\0';
}
else
{
PDBG (pixma_dbg (1, "WARNING:No serial number\n"));
}
done:
sanei_usb_close (usb);
}
static int
map_error (SANE_Status ss)
{
switch (ss)
{
case SANE_STATUS_GOOD:
return 0;
case SANE_STATUS_UNSUPPORTED:
return -ENOTSUP;
case SANE_STATUS_CANCELLED:
return -ECANCELED;
case SANE_STATUS_DEVICE_BUSY:
return -EBUSY;
case SANE_STATUS_INVAL:
return -EINVAL;
case SANE_STATUS_EOF:
return 0; /* FIXME: */
case SANE_STATUS_JAMMED:
return -EDEADLK;
case SANE_STATUS_NO_DOCS:
return -ENODATA;
case SANE_STATUS_COVER_OPEN:
return -ENOLCK;
case SANE_STATUS_IO_ERROR:
return -EIO;
case SANE_STATUS_NO_MEM:
return -ENOMEM;
case SANE_STATUS_ACCESS_DENIED:
return -EACCES;
default:
PDBG (pixma_dbg (1, "WARNING:Unmapped SANE Status code %d\n", ss));
return -ENOTSUP; /* should not happen */
}
}
int
pixma_io_init (void)
{
sanei_usb_init ();
nscanners = 0;
return 0;
}
void
pixma_io_cleanup (void)
{
while (first_io)
pixma_disconnect (first_io);
clear_scanner_list ();
}
int
pixma_collect_devices (const struct pixma_config_t *const pixma_devices[])
{
unsigned i, j;
scanner_info_t *si;
const pixma_config_t *cfg;
clear_scanner_list ();
j = 0;
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)
{
si->cfg = cfg;
read_serial_number (si);
si = si->next;
j++;
}
}
}
return nscanners;
}
const pixma_config_t *
pixma_get_device_config (unsigned devnr)
{
const scanner_info_t *si = get_scanner_info (devnr);
return (si) ? si->cfg : NULL;
}
const char *
pixma_get_device_id (unsigned devnr)
{
const scanner_info_t *si = get_scanner_info (devnr);
return (si) ? si->serial : NULL;
}
int
pixma_connect (unsigned devnr, pixma_io_t ** handle)
{
pixma_io_t *io;
SANE_Int usb;
const scanner_info_t *si;
int error;
*handle = NULL;
si = get_scanner_info (devnr);
if (!si)
return -EINVAL;
error = map_error (sanei_usb_open (si->devname, &usb));
if (error < 0)
return error;
io = (pixma_io_t *) calloc (1, sizeof (*io));
if (!io)
{
sanei_usb_close (usb);
return -ENOMEM;
}
io->next = first_io;
first_io = io;
io->usb = usb;
*handle = io;
return 0;
}
void
pixma_disconnect (pixma_io_t * io)
{
pixma_io_t **p;
if (!io)
return;
for (p = &first_io; *p && *p != io; p = &((*p)->next))
{
}
PASSERT (*p);
if (!(*p))
return;
sanei_usb_close (io->usb);
*p = io->next;
free (io);
}
int
pixma_reset_device (pixma_io_t * io)
{
UNUSED (io);
return -ENOTSUP;
}
int
pixma_write (pixma_io_t * io, const void *cmd, unsigned len)
{
size_t count = len;
int error;
#ifdef HAVE_SANEI_USB_SET_TIMEOUT
sanei_usb_set_timeout (PIXMA_BULKOUT_TIMEOUT);
#endif
error = map_error (sanei_usb_write_bulk (io->usb, cmd, &count));
if (error == -EIO)
error = -ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */
if (error == 0)
error = count;
PDBG (pixma_dump (10, "OUT ", cmd, error, len, 128));
return error;
}
int
pixma_read (pixma_io_t * io, void *buf, unsigned size)
{
size_t count = size;
int error;
#ifdef HAVE_SANEI_USB_SET_TIMEOUT
sanei_usb_set_timeout (PIXMA_BULKIN_TIMEOUT);
#endif
error = map_error (sanei_usb_read_bulk (io->usb, buf, &count));
if (error == -EIO)
error = -ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */
if (error == 0)
error = count;
PDBG (pixma_dump (10, "IN ", buf, error, -1, 128));
return error;
}
int
pixma_wait_interrupt (pixma_io_t * io, void *buf, unsigned size, int timeout)
{
size_t count = size;
int error;
#ifdef HAVE_SANEI_USB_SET_TIMEOUT
/* FIXME: What is the meaning of "timeout" in sanei_usb? */
if (timeout < 0)
timeout = INT_MAX;
else if (timeout == 0)
timeout = 1;
sanei_usb_set_timeout (timeout);
#endif
error = map_error (sanei_usb_read_int (io->usb, buf, &count));
if (error == -EIO)
error = -ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */
if (error == 0)
error = count;
PDBG (pixma_dump (10, "INTR", buf, error, -1, -1));
return error;
}
int
pixma_set_interrupt_mode (pixma_io_t * s, int background)
{
UNUSED (s);
return (background) ? -ENOTSUP : 0;
}

Wyświetl plik

@ -0,0 +1,679 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
/* test cases
1. short USB packet (must be no -ETIMEDOUT)
2. cancel using button on the printer (look for abort command)
3. start scan while busy (status 0x1414)
4. cancel using ctrl-c (must send abort command)
*/
#include "../include/sane/config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> /* localtime(C90) */
#include <errno.h> /* POSIX */
#include "pixma_rename.h"
#include "pixma_common.h"
#include "pixma_io.h"
#ifdef __GNUC__
# define UNUSED(v) (void) v
#else
# define UNUSED(v)
#endif
/* Size of the command buffer should be multiple of wMaxPacketLength and
greater than 4096+24.
4096 = size of gamma table. 24 = header + checksum */
#define IMAGE_BLOCK_SIZE (512*1024)
#define CMDBUF_SIZE (4096 + 24)
#define DEFAULT_GAMMA 1.0
enum mp150_state_t
{
state_idle,
state_warmup,
state_scanning,
state_transfering,
state_finished
};
enum mp150_cmd_t
{
cmd_start_session = 0xdb20,
cmd_select_source = 0xdd20,
cmd_gamma = 0xee20,
cmd_scan_param = 0xde20,
cmd_status = 0xf320,
cmd_abort_session = 0xef20,
cmd_time = 0xeb80,
cmd_read_image = 0xd420
};
typedef struct mp150_t
{
enum mp150_state_t state;
pixma_cmdbuf_t cb;
uint8_t *imgbuf;
uint8_t current_status[12];
unsigned last_block:1;
} mp150_t;
/*
STAT: 0x0606 = ok,
0x1515 = failed (-ECANCELED),
0x1414 = busy (-EBUSY)
Transaction scheme
1. command_header/data | result_header
2. command_header | result_header/data
3. command_header | result_header/image_data
- data has checksum in the last byte.
- image_data has no checksum.
- data and image_data begins in the same USB packet as
command_header or result_header.
command format #1:
u16be cmd
u8[6] 0
u8[4] 0
u32be PLEN parameter length
u8[PLEN-1] parameter
u8 parameter check sum
result:
u16be STAT
u8 0
u8 0 or 0x21 if STAT == 0x1414
u8[4] 0
command format #2:
u16be cmd
u8[6] 0
u8[4] 0
u32be RLEN result length
result:
u16be STAT
u8[6] 0
u8[RLEN-1] result
u8 result check sum
command format #3: (only used by read_image_block)
u16be 0xd420
u8[6] 0
u8[4] 0
u32be max. block size + 8
result:
u16be STAT
u8[6] 0
u8 block info byte 0x38 = last image data block
u8[3] 0
u32be ILEN image data size
u8[ILEN] image data
*/
static void mp150_finish_scan (pixma_t * s);
static int
is_calibrated (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
return (mp->current_status[8] == 1);
}
static int
has_paper (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
return (mp->current_status[1] == 0); /* FIXME: correct? */
}
static void
drain_bulk_in (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) ==
IMAGE_BLOCK_SIZE);
}
static int
abort_session (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
}
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)
{
mp150_t *mp = (mp150_t *) s->subdriver;
uint8_t *data;
data = pixma_newcmd (&mp->cb, cmd_select_source, 12, 0);
data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1;
data[1] = 1;
return pixma_exec (s, &mp->cb);
}
static int
send_gamma_table (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
const uint8_t *lut = s->param->gamma_table;
uint8_t *data;
data = pixma_newcmd (&mp->cb, cmd_gamma, 4096 + 8, 0);
data[0] = (s->param->channels == 3) ? 0x10 : 0x01;
pixma_set_be16 (0x1004, data + 2);
if (lut)
memcpy (data + 4, lut, 4096);
else
pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096);
return pixma_exec (s, &mp->cb);
}
static int
send_scan_param (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
uint8_t *data;
#if 0
unsigned raw_width;
if (s->param->channels == 1)
raw_width = ALIGN (s->param->w, 12);
else
raw_width = ALIGN (s->param->w, 4);
/* FIXME: MP150 can cope with "unaligned" width. It always correctly
returns padded rows. Can other models do this, too? */
#endif
data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x30, 0);
pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04);
pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06);
pixma_set_be32 (s->param->x, data + 0x08);
pixma_set_be32 (s->param->y, data + 0x0c);
pixma_set_be32 (s->param->w, 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; /* bits per pixel */
data[0x20] = 0xff;
data[0x23] = 0x81;
data[0x26] = 0x02;
data[0x27] = 0x01;
return pixma_exec (s, &mp->cb);
}
static int
query_status (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
uint8_t *data;
int error;
data = pixma_newcmd (&mp->cb, cmd_status, 0, 12);
error = pixma_exec (s, &mp->cb);
if (error >= 0)
{
memcpy (mp->current_status, data, 12);
PDBG (pixma_dbg (3, "Current status: %s %s\n",
is_calibrated (s) ? "Calibrated" : "Calibrating",
has_paper (s) ? "HasPaper" : "NoPaper"));
}
return error;
}
static int
send_time (pixma_t * s)
{
/* Why does a scanner need a time? */
time_t now;
struct tm *t;
uint8_t *data;
mp150_t *mp = (mp150_t *) s->subdriver;
data = pixma_newcmd (&mp->cb, cmd_time, 20, 0);
pixma_get_time (&now, NULL);
t = localtime (&now);
snprintf ((char *) data, 16,
"%02d/%02d/%02d %02d:%02d",
t->tm_year % 100, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min);
PDBG (pixma_dbg (3, "Sending time: '%s'\n", (char *) data));
return pixma_exec (s, &mp->cb);
}
/* TODO: Simplify this function. Read the whole data packet in one shot. */
static int
read_image_block (pixma_t * s, uint8_t * header, uint8_t * data)
{
static const uint8_t cmd[16] = /* 0xd420 cmd */
{ 0xd4, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
IMAGE_BLOCK_SIZE / 65536, 0, 8
};
mp150_t *mp = (mp150_t *) s->subdriver;
const int hlen = 8 + 8;
int error, datalen;
mp->state = state_transfering;
mp->cb.reslen =
pixma_cmd_transaction (s, cmd, sizeof (cmd), mp->cb.buf, 512);
datalen = mp->cb.reslen;
if (datalen < 0)
return datalen;
memcpy (header, mp->cb.buf, hlen);
if (datalen >= hlen)
{
datalen -= hlen;
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;
}
}
mp->state = state_scanning;
mp->cb.expected_reslen = 0;
error = pixma_check_result (&mp->cb);
if (error < 0)
return error;
if (mp->cb.reslen < hlen)
return -EPROTO;
return datalen;
}
/*
handle_interrupt() waits until it receives an interrupt packet or times out.
It calls send_time() and query_status() if necessary. Therefore, make sure
that handle_interrupt() is only called from a safe context for send_time()
and query_status().
Returns:
0 timed out
1 interrupt packet received
-EINTR interrupted by signal
<0 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 == -ETIMEDOUT)
return 0;
if (len < 0)
return len;
if (len != 16)
{
PDBG (pixma_dbg (1, "WARNING:unexpected interrupt packet length %d\n",
len));
return -EPROTO;
}
switch (buf[0])
{
case 0:
/* no button pressed */
break;
case 1:
s->events = PIXMA_EV_BUTTON1 | buf[1];
break;
case 2:
s->events = PIXMA_EV_BUTTON2 | buf[1];
break;
default:
PDBG (pixma_dbg (2, "Interesting interrupt packet\n"));
PDBG (pixma_hexdump (2, buf, 16));
}
/* More than one event can be reported at the same time. */
if (buf[3] & 1)
send_time (s);
if (buf[9] & 2)
query_status (s);
return 1;
}
static int
wait_until_ready (pixma_t * s)
{
int error, tmo = 60;
error = query_status (s);
if (error < 0)
return error;
while (!is_calibrated (s))
{
error = handle_interrupt (s, 1000);
if (s->cancel)
return -ECANCELED;
if (error != -EINTR && error < 0)
return error;
if (--tmo == 0)
{
PDBG (pixma_dbg (1, "WARNING:Timed out in wait_until_ready()\n"));
return -ETIMEDOUT;
}
#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;
#endif
}
return 0;
}
static int
mp150_open (pixma_t * s)
{
mp150_t *mp;
uint8_t *buf;
mp = (mp150_t *) calloc (1, sizeof (*mp));
if (!mp)
return -ENOMEM;
buf = (uint8_t *) malloc (CMDBUF_SIZE + IMAGE_BLOCK_SIZE);
if (!buf)
{
free (mp);
return -ENOMEM;
}
s->subdriver = mp;
mp->state = state_idle;
mp->cb.buf = buf;
mp->cb.size = CMDBUF_SIZE;
mp->cb.res_header_len = 8;
mp->cb.cmd_header_len = 16;
mp->cb.cmd_len_field_ofs = 14;
mp->imgbuf = buf + CMDBUF_SIZE;
handle_interrupt (s, 200);
return 0;
}
static void
mp150_close (pixma_t * s)
{
mp150_t *mp = (mp150_t *) s->subdriver;
mp150_finish_scan (s);
free (mp->cb.buf);
free (mp);
s->subdriver = NULL;
}
static int
mp150_check_param (pixma_t * s, pixma_scan_param_t * sp)
{
unsigned raw_width;
UNUSED (s);
sp->depth = 8; /* MP150 only supports 8 bit per channel. */
if (sp->channels == 1)
raw_width = ALIGN (sp->w, 12);
else
raw_width = ALIGN (sp->w, 4);
sp->line_size = raw_width * sp->channels;
return 0;
}
static int
mp150_scan (pixma_t * s)
{
int error, tmo;
mp150_t *mp = (mp150_t *) s->subdriver;
if (mp->state != state_idle)
return -EBUSY;
while (handle_interrupt (s, 0) > 0)
{
} /* clear interrupt packets buffer */
if (s->param->source == PIXMA_SOURCE_ADF)
{
error = query_status (s);
if (error < 0)
return error;
#ifndef NDEBUG
pixma_dbg (1,
"Support for ADF is untested. If it doesn't work, please set\n");
pixma_dbg (1,
"the debug level to 10 and send me debug messages for these\n");
pixma_dbg (1, "2 cases:\n");
pixma_dbg (1, " 1. no paper in ADF\n");
pixma_dbg (1, " 2. paper in ADF\n");
#endif
if (!has_paper (s))
return -ENODATA;
}
error = start_session (s);
tmo = 10;
while (error == -EBUSY && --tmo >= 0)
{
if (s->cancel)
{
error = -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 == -EBUSY || error == -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->state = state_warmup;
if (error >= 0)
error = select_source (s);
if (error >= 0)
error = send_gamma_table (s);
if (error >= 0)
error = send_scan_param (s);
if (error < 0)
{
mp150_finish_scan (s);
return error;
}
return 0;
}
static int
mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
{
int error;
mp150_t *mp = (mp150_t *) s->subdriver;
unsigned block_size, bytes_received;
uint8_t header[16];
if (mp->state == state_warmup)
{
error = wait_until_ready (s);
if (error < 0)
return error;
pixma_sleep (1500000); /* No need to wait, actually, but Window's driver
* waits 1.5 sec. */
mp->state = state_scanning;
mp->last_block = 0;
}
do
{
if (s->cancel)
return -ECANCELED;
if (mp->last_block)
{
/* end of image */
mp->state = state_finished;
return 0;
}
error = read_image_block (s, header, mp->imgbuf);
if (error < 0)
return error;
bytes_received = error;
block_size = pixma_get_be32 (header + 12);
mp->last_block = (header[8] == 0x38);
if (header[8] != 0 && header[8] != 0x38)
{
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);
}
}
while (block_size == 0);
ib->rptr = mp->imgbuf;
ib->rend = mp->imgbuf + bytes_received;
return ib->rend - ib->rptr;
}
static void
mp150_finish_scan (pixma_t * s)
{
int error;
mp150_t *mp = (mp150_t *) s->subdriver;
switch (mp->state)
{
case state_transfering:
drain_bulk_in (s);
/* fall through */
case state_scanning:
case state_warmup:
error = abort_session (s);
if (error < 0)
PDBG (pixma_dbg (1, "WARNING:abort_session() failed: %s\n",
strerror (-error)));
/* fall through */
case state_finished:
mp->state = state_idle;
/* fall through */
case state_idle:
break;
}
}
static void
mp150_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 const pixma_scan_ops_t pixma_mp150_ops = {
mp150_open,
mp150_close,
mp150_scan,
mp150_fill_buffer,
mp150_finish_scan,
mp150_wait_event,
mp150_check_param
};
#define DEVICE(name, pid, dpi, cap) { \
name, /* name */ \
0x04a9, 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 \
}
const pixma_config_t pixma_mp150_devices[] = {
DEVICE ("Canon PIXMA MP150", 0x1709, 1200, 0),
DEVICE ("Canon PIXMA MP170", 0x170a, 1200, 0),
DEVICE ("Canon PIXMA MP450", 0x170b, 1200, 0),
DEVICE ("Canon PIXMA MP500", 0x170c, 1200, 0),
/* TODO: check MP830 & MP800 limits & cap */
DEVICE ("Canon PIXMA MP800", 0x170d, 2400,
PIXMA_CAP_ADF | PIXMA_CAP_EXPERIMENT),
DEVICE ("Canon PIXMA MP830", 0x1713, 2400,
PIXMA_CAP_ADF | PIXMA_CAP_EXPERIMENT),
DEVICE (NULL, 0, 0, 0)
};

Wyświetl plik

@ -0,0 +1,490 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#include "../include/sane/config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h> /* POSIX */
#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 (0xc000)
#define CMDBUF_SIZE 512
enum mp730_state_t
{
state_idle,
state_warmup,
state_scanning,
state_transfering,
state_finished
};
enum mp730_cmd_t
{
cmd_start_session = 0xdb20,
cmd_select_source = 0xdd20,
cmd_gamma = 0xee20,
cmd_scan_param = 0xde20,
cmd_status = 0xf320,
cmd_abort_session = 0xef20,
cmd_time = 0xeb80,
cmd_read_image = 0xd420,
cmd_activate = 0xcf60
};
typedef struct mp730_t
{
enum mp730_state_t state;
pixma_cmdbuf_t cb;
unsigned raw_width;
uint8_t current_status[12];
uint8_t *buf, *imgbuf, *lbuf;
unsigned imgbuf_len;
unsigned last_block:1;
} mp730_t;
static void mp730_finish_scan (pixma_t * s);
static int
has_paper (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
return (mp->current_status[1] == 0);
}
static void
drain_bulk_in (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) ==
IMAGE_BLOCK_SIZE);
}
static int
abort_session (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
}
static int
query_status (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
uint8_t *data;
int error;
data = pixma_newcmd (&mp->cb, cmd_status, 0, 12);
error = pixma_exec (s, &mp->cb);
if (error >= 0)
{
memcpy (mp->current_status, data, 12);
}
return error;
}
static int
activate (pixma_t * s, uint8_t x)
{
mp730_t *mp = (mp730_t *) s->subdriver;
uint8_t *data = pixma_newcmd (&mp->cb, cmd_activate, 10, 0);
data[0] = 1;
data[3] = x;
return pixma_exec (s, &mp->cb);
}
static int
start_session (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session);
}
static int
select_source (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
uint8_t *data = pixma_newcmd (&mp->cb, cmd_select_source, 10, 0);
data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1;
return pixma_exec (s, &mp->cb);
}
static int
send_scan_param (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
uint8_t *data;
unsigned mode;
data = pixma_newcmd (&mp->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 (mp->raw_width, data + 0x10);
pixma_set_be32 (s->param->h, data + 0x14);
mode = (s->param->channels == 1) ? 0x0408 : 0x0818;
pixma_set_be16 (mode, data + 0x18);
data[0x1f] = 0x7f;
data[0x20] = 0xff;
data[0x23] = 0x81;
return pixma_exec (s, &mp->cb);
}
static int
read_image_block (pixma_t * s, uint8_t * header, uint8_t * data)
{
static const uint8_t cmd[10] = /* 0xd420 cmd */
{ 0xd4, 0x20, 0, 0, 0, 0, 0, IMAGE_BLOCK_SIZE / 256, 4, 0 };
mp730_t *mp = (mp730_t *) s->subdriver;
const int hlen = 2 + 4;
int error, datalen;
mp->state = state_transfering;
mp->cb.reslen =
pixma_cmd_transaction (s, cmd, sizeof (cmd), mp->cb.buf, 512);
datalen = mp->cb.reslen;
if (datalen < 0)
return datalen;
memcpy (header, mp->cb.buf, hlen);
if (datalen >= hlen)
{
datalen -= hlen;
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;
}
}
mp->state = state_scanning;
mp->cb.expected_reslen = 0;
error = pixma_check_result (&mp->cb);
if (error < 0)
return error;
if (mp->cb.reslen < hlen)
return -EPROTO;
return datalen;
}
static int
step1 (pixma_t * s)
{
int error;
error = query_status (s);
if (error < 0)
return error;
if (s->param->source == PIXMA_SOURCE_ADF && !has_paper (s))
return -ENODATA;
if (error >= 0)
error = activate (s, 0);
if (error >= 0)
error = activate (s, 4);
return error;
}
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
mp730_open (pixma_t * s)
{
mp730_t *mp;
uint8_t *buf;
mp = (mp730_t *) calloc (1, sizeof (*mp));
if (!mp)
return -ENOMEM;
buf = (uint8_t *) malloc (CMDBUF_SIZE);
if (!buf)
{
free (mp);
return -ENOMEM;
}
s->subdriver = mp;
mp->state = state_idle;
mp->cb.buf = buf;
mp->cb.size = CMDBUF_SIZE;
mp->cb.res_header_len = 2;
mp->cb.cmd_header_len = 10;
mp->cb.cmd_len_field_ofs = 7;
return 0;
}
static void
mp730_close (pixma_t * s)
{
mp730_t *mp = (mp730_t *) s->subdriver;
mp730_finish_scan (s);
free (mp->cb.buf);
free (mp);
s->subdriver = NULL;
}
static unsigned
calc_raw_width (const pixma_t * s, const pixma_scan_param_t * sp)
{
unsigned raw_width, maxw;
/* FIXME: Does MP730 need the alignment? */
if (sp->channels == 1)
raw_width = ALIGN (sp->w, 12);
else
raw_width = ALIGN (sp->w, 4);
maxw = s->cfg->width * (sp->xdpi / 75);
return (raw_width < maxw) ? raw_width : maxw;
}
static int
mp730_check_param (pixma_t * s, pixma_scan_param_t * sp)
{
unsigned raw_width;
UNUSED (s);
sp->depth = 8; /* FIXME: Does MP730 supports other depth? */
raw_width = calc_raw_width (s, sp);
sp->line_size = raw_width * sp->channels;
return 0;
}
static int
mp730_scan (pixma_t * s)
{
int error, n;
mp730_t *mp = (mp730_t *) s->subdriver;
uint8_t *buf;
if (mp->state != state_idle)
return -EBUSY;
mp->raw_width = calc_raw_width (s, s->param);
n = IMAGE_BLOCK_SIZE / s->param->line_size + 1;
buf = (uint8_t *) malloc ((n + 1) * s->param->line_size + IMAGE_BLOCK_SIZE);
if (!buf)
return -ENOMEM;
mp->buf = buf;
mp->lbuf = buf;
mp->imgbuf = buf + n * s->param->line_size;
mp->imgbuf_len = 0;
error = step1 (s);
if (error >= 0)
error = start_session (s);
if (error >= 0)
mp->state = state_scanning;
if (error >= 0)
error = select_source (s);
if (error >= 0)
error = send_scan_param (s);
if (error < 0)
{
mp730_finish_scan (s);
return error;
}
mp->last_block = 0;
return 0;
}
static int
mp730_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
{
int error, n;
mp730_t *mp = (mp730_t *) s->subdriver;
unsigned block_size, bytes_received;
uint8_t header[16];
do
{
do
{
if (s->cancel)
return -ECANCELED;
if (mp->last_block)
{
/* end of image */
mp->state = state_finished;
return 0;
}
error = read_image_block (s, header, mp->imgbuf + mp->imgbuf_len);
if (error < 0)
return error;
bytes_received = error;
block_size = pixma_get_be16 (header + 4);
mp->last_block = (header[2] == 0x38);
if (header[2] != 0 && header[2] != 0x38)
{
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 (100000); /* FIXME: too short, too long? */
}
}
while (block_size == 0);
mp->imgbuf_len += bytes_received;
n = mp->imgbuf_len / s->param->line_size;
/* n = number of full lines (rows) we have in the buffer. */
if (n != 0)
{
pack_rgb (mp->imgbuf, n, mp->raw_width, mp->lbuf);
block_size = n * s->param->line_size;
mp->imgbuf_len -= block_size;
memcpy (mp->imgbuf, mp->imgbuf + block_size, mp->imgbuf_len);
}
}
while (n == 0);
ib->rptr = mp->lbuf;
ib->rend = mp->lbuf + block_size;
return ib->rend - ib->rptr;
}
static void
mp730_finish_scan (pixma_t * s)
{
int error;
mp730_t *mp = (mp730_t *) s->subdriver;
switch (mp->state)
{
case state_transfering:
drain_bulk_in (s);
/* fall through */
case state_scanning:
case state_warmup:
error = abort_session (s);
if (error < 0)
PDBG (pixma_dbg (1, "WARNING:abort_session() failed: %s\n",
strerror (-error)));
/* fall through */
case state_finished:
query_status (s);
query_status (s);
activate (s, 0);
free (mp->buf);
mp->buf = mp->lbuf = mp->imgbuf = NULL;
mp->state = state_idle;
/* fall through */
case state_idle:
break;
}
}
static const pixma_scan_ops_t pixma_mp730_ops = {
mp730_open,
mp730_close,
mp730_scan,
mp730_fill_buffer,
mp730_finish_scan,
NULL /*mp730_wait_event */ ,
mp730_check_param
};
#define DEVICE(name, pid, dpi, w, h, cap) { \
name, /* name */ \
0x04a9, pid, /* vid pid */ \
1, /* iface */ \
&pixma_mp730_ops, /* ops */ \
dpi, dpi, /* xdpi, ydpi */ \
w, h, /* width, height */ \
cap \
}
const pixma_config_t pixma_mp730_devices[] = {
/* TODO: check area limits */
DEVICE ("Canon MultiPASS MP700", 0x2630, 1200, 637, 877, 0),
DEVICE ("Canon MultiPASS MP730", 0x262f, 1200, 637, 870,
PIXMA_CAP_ADF | PIXMA_CAP_EXPERIMENT),
DEVICE (NULL, 0, 0, 0, 0, 0)
};

Wyświetl plik

@ -0,0 +1,714 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#include "../include/sane/config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h> /* POSIX */
#include "pixma_rename.h"
#include "pixma_common.h"
#include "pixma_io.h"
/* Credits should go to Martin Schewe (http://pixma.schewe.com) who analysed
the protocol of MP750. */
/* TODO: remove lines marked with SIM. They are inserted so that I can test
the subdriver with the simulator. WY */
#ifdef __GNUC__
# define UNUSED(v) (void) v
#else
# define UNUSED(v)
#endif
#define IMAGE_BLOCK_SIZE 0xc000
#define CMDBUF_SIZE 512
#define HAS_PAPER(s) (s[1] == 0)
#define IS_WARMING_UP(s) (s[7] != 3)
#define IS_CALIBRATED(s) (s[8] == 0xf)
enum mp750_state_t
{
state_idle,
state_warmup,
state_scanning,
state_transfering,
state_finished
};
enum mp750_cmd_t
{
cmd_start_session = 0xdb20,
cmd_select_source = 0xdd20,
cmd_scan_param = 0xde20,
cmd_status = 0xf320,
cmd_abort_session = 0xef20,
cmd_time = 0xeb80,
cmd_read_image = 0xd420,
cmd_activate = 0xcf60,
cmd_calibrate = 0xe920,
cmd_reset = 0xff20
};
typedef struct mp750_t
{
enum mp750_state_t state;
pixma_cmdbuf_t cb;
unsigned raw_width, raw_height;
uint8_t current_status[12];
uint8_t *buf, *rawimg, *img;
unsigned rawimg_left, imgbuf_len, last_block_size, imgbuf_ofs;
int shifted_bytes;
unsigned last_block;
unsigned monochrome:1;
unsigned needs_time:1;
unsigned needs_abort:1;
} mp750_t;
static void mp750_finish_scan (pixma_t * s);
static void check_status (pixma_t * s);
static int
has_paper (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
return HAS_PAPER (mp->current_status);
}
static int
is_warming_up (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
return IS_WARMING_UP (mp->current_status);
}
static int
is_calibrated (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
return IS_CALIBRATED (mp->current_status);
}
static void
drain_bulk_in (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
while (pixma_read (s->io, mp->buf, IMAGE_BLOCK_SIZE) == IMAGE_BLOCK_SIZE);
}
static int
abort_session (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session);
}
static int
query_status (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
uint8_t *data;
int error;
data = pixma_newcmd (&mp->cb, cmd_status, 0, 12);
error = pixma_exec (s, &mp->cb);
if (error >= 0)
{
memcpy (mp->current_status, data, 12);
PDBG (pixma_dbg (2, "Current status: %s %s %s\n",
has_paper (s) ? "HasPaper" : "NoPaper",
is_warming_up (s) ? "WarmingUp" : "LampReady",
is_calibrated (s) ? "Calibrated" : "Calibrating"));
}
return error;
}
static int
activate (pixma_t * s, uint8_t x)
{
mp750_t *mp = (mp750_t *) s->subdriver;
uint8_t *data = pixma_newcmd (&mp->cb, cmd_activate, 10, 0);
data[0] = 1;
data[3] = x;
return pixma_exec (s, &mp->cb);
}
static int
activate_cs (pixma_t * s, uint8_t x)
{
/*SIM*/ check_status (s);
return activate (s, x);
}
static int
start_session (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session);
}
static int
select_source (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
uint8_t *data = pixma_newcmd (&mp->cb, cmd_select_source, 10, 0);
data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1;
data[1] = 1;
return pixma_exec (s, &mp->cb);
}
static int
send_scan_param (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
uint8_t *data;
unsigned mode;
mode = 0x0818;
data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x2e, 0);
pixma_set_be16 (s->param->xdpi | 0x8000, data + 4);
pixma_set_be16 (s->param->ydpi | 0x8000, data + 6);
pixma_set_be32 (s->param->x, data + 8);
pixma_set_be32 (s->param->y, data + 12);
pixma_set_be32 (mp->raw_width, data + 16);
pixma_set_be32 (mp->raw_height, data + 20);
pixma_set_be16 (mode, data + 24);
data[32] = 0xff;
data[35] = 0x81;
data[38] = 0x02;
data[39] = 0x01;
data[41] = mp->monochrome ? 0 : 1;
return pixma_exec (s, &mp->cb);
}
static int
calibrate (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
return pixma_exec_short_cmd (s, &mp->cb, cmd_calibrate);
}
static int
calibrate_cs (pixma_t * s)
{
/*SIM*/ check_status (s);
return calibrate (s);
}
static int
reset_scanner (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
pixma_newcmd (&mp->cb, cmd_reset, 0, 16);
return pixma_exec (s, &mp->cb);
}
static int
request_image_block_ex (pixma_t * s, unsigned *size, uint8_t * info,
unsigned flag)
{
mp750_t *mp = (mp750_t *) s->subdriver;
int error;
memset (mp->cb.buf, 0, 10);
pixma_set_be16 (cmd_read_image, mp->cb.buf);
mp->cb.buf[7] = *size >> 8;
mp->cb.buf[8] = 4 | flag;
mp->cb.reslen = pixma_cmd_transaction (s, mp->cb.buf, 10, mp->cb.buf, 6);
mp->cb.expected_reslen = 0;
error = pixma_check_result (&mp->cb);
if (error >= 0)
{
if (mp->cb.reslen == 6)
{
*info = mp->cb.buf[2];
*size = pixma_get_be16 (mp->cb.buf + 4);
}
else
{
error = -EPROTO;
}
}
return error;
}
static int
request_image_block (pixma_t * s, unsigned *size, uint8_t * info)
{
return request_image_block_ex (s, size, info, 0);
}
static int
request_image_block2 (pixma_t * s, uint8_t * info)
{
unsigned temp = 0;
return request_image_block_ex (s, &temp, info, 0x20);
}
static int
read_image_block (pixma_t * s, uint8_t * data)
{
int count, temp;
count = pixma_read (s->io, data, IMAGE_BLOCK_SIZE);
if (count < 0)
return count;
if (count == IMAGE_BLOCK_SIZE)
pixma_read (s->io, &temp, 0);
return count;
}
static int
handle_interrupt (pixma_t * s, int timeout)
{
mp750_t *mp = (mp750_t *) s->subdriver;
int error;
uint8_t intr[16];
error = pixma_wait_interrupt (s->io, intr, sizeof (intr), timeout);
if (error == -ETIMEDOUT)
return 0;
if (error < 0)
return error;
if (error != 16)
{
PDBG (pixma_dbg (1, "WARNING:unexpected interrupt packet length %d\n",
error));
return -EPROTO;
}
if (intr[10] & 0x40)
mp->needs_time = 1;
if (intr[12] & 0x40)
query_status (s);
return 1;
}
static void
check_status (pixma_t * s)
{
while (handle_interrupt (s, 0) > 0);
}
static int
step1 (pixma_t * s)
{
int error;
error = activate (s, 0);
if (error >= 0)
error = query_status (s);
if (error >= 0)
{
if (s->param->source == PIXMA_SOURCE_ADF && !has_paper (s))
return -ENODATA;
}
if (error >= 0)
error = activate_cs (s, 0);
/*SIM*/ if (error >= 0)
error = activate_cs (s, 0x20);
if (error >= 0)
error = calibrate_cs (s);
return error;
}
static void
shift_rgb (const uint8_t * src, unsigned pixels,
int sr, int sg, int sb, uint8_t * dst)
{
for (; pixels != 0; pixels--)
{
*(dst++ + sr) = *src++;
*(dst++ + sg) = *src++;
*(dst++ + sb) = *src++;
}
}
static int
mp750_open (pixma_t * s)
{
mp750_t *mp;
uint8_t *buf;
mp = (mp750_t *) calloc (1, sizeof (*mp));
if (!mp)
return -ENOMEM;
buf = (uint8_t *) malloc (CMDBUF_SIZE);
if (!buf)
{
free (mp);
return -ENOMEM;
}
s->subdriver = mp;
mp->state = state_idle;
/* ofs: 0 1 2 3 4 5 6 7 8 9
cmd: cmd1 cmd2 00 00 00 00 00 00 00 00
data length-^^^^^ => cmd_len_field_ofs
|--------- cmd_header_len --------|
res: res1 res2
|---------| res_header_len
*/
mp->cb.buf = buf;
mp->cb.size = CMDBUF_SIZE;
mp->cb.res_header_len = 2;
mp->cb.cmd_header_len = 10;
mp->cb.cmd_len_field_ofs = 7;
return 0;
}
static void
mp750_close (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
mp750_finish_scan (s);
free (mp->cb.buf);
free (mp);
s->subdriver = NULL;
}
static int
mp750_check_param (pixma_t * s, pixma_scan_param_t * sp)
{
unsigned raw_width;
UNUSED (s);
sp->depth = 8; /* FIXME: Does MP750 supports other depth? */
if (sp->channels == 1)
raw_width = ALIGN (sp->w, 12);
else
raw_width = ALIGN (sp->w, 4);
sp->line_size = raw_width * sp->channels;
return 0;
}
static int
mp750_scan (pixma_t * s)
{
mp750_t *mp = (mp750_t *) s->subdriver;
int error;
uint8_t *buf;
unsigned size, dpi, spare;
if (mp->state != state_idle)
return -EBUSY;
if (s->param->channels == 1)
mp->raw_width = ALIGN (s->param->w, 12);
else
mp->raw_width = ALIGN (s->param->w, 4);
dpi = s->param->ydpi;
spare = 4 * dpi / 75 + 1;
mp->raw_height = s->param->h + spare;
PDBG (pixma_dbg (3, "raw_width=%u raw_height=%u dpi=%u\n",
mp->raw_width, mp->raw_height, dpi));
size = 8 + 2 * IMAGE_BLOCK_SIZE + spare * s->param->line_size;
buf = (uint8_t *) malloc (size);
if (!buf)
return -ENOMEM;
mp->buf = buf;
mp->rawimg = buf;
mp->imgbuf_ofs = spare * s->param->line_size;
mp->img = mp->rawimg + IMAGE_BLOCK_SIZE + 8;
mp->imgbuf_len = IMAGE_BLOCK_SIZE + mp->imgbuf_ofs;
mp->rawimg_left = 0;
mp->last_block_size = 0;
mp->shifted_bytes = -(int) mp->imgbuf_ofs;
error = step1 (s);
if (error >= 0)
error = start_session (s);
if (error >= 0)
mp->state = state_warmup;
if (error >= 0)
error = select_source (s);
if (error >= 0)
error = send_scan_param (s);
if (error < 0)
{
mp750_finish_scan (s);
return error;
}
return 0;
}
static int
mp750_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib)
{
mp750_t *mp = (mp750_t *) s->subdriver;
int error;
uint8_t info;
unsigned block_size, bytes_received, n;
int shift[3], base_shift;
if (mp->state == state_warmup)
{
int tmo = 60;
query_status (s);
check_status (s);
/*SIM*/ while (!is_calibrated (s) && --tmo >= 0)
{
if (s->cancel)
return -ECANCELED;
if (handle_interrupt (s, 1000) > 0)
{
block_size = 0;
error = request_image_block (s, &block_size, &info);
/*SIM*/ if (error < 0)
return error;
}
}
if (tmo < 0)
{
PDBG (pixma_dbg (1, "WARNING:Timed out waiting for calibration\n"));
return -ETIMEDOUT;
}
pixma_sleep (100000);
query_status (s);
if (is_warming_up (s) || !is_calibrated (s))
{
PDBG (pixma_dbg (1, "WARNING:Wrong status: wup=%d cal=%d\n",
is_warming_up (s), is_calibrated (s)));
return -EPROTO;
}
block_size = 0;
request_image_block (s, &block_size, &info);
/*SIM*/ mp->state = state_scanning;
mp->last_block = 0;
}
/* TODO: Move to other place, values are constant. */
base_shift = 2 * s->param->ydpi / 75 * s->param->line_size;
if (s->param->source == PIXMA_SOURCE_ADF)
{
shift[0] = 0;
shift[1] = -base_shift;
shift[2] = -2 * base_shift;
}
else
{
shift[0] = -2 * base_shift;
shift[1] = -base_shift;
shift[2] = 0;
}
do
{
if (mp->last_block_size > 0)
{
block_size = mp->imgbuf_len - mp->last_block_size;
memcpy (mp->img, mp->img + mp->last_block_size, block_size);
}
do
{
if (s->cancel)
return -ECANCELED;
if (mp->last_block)
{
/* end of image */
info = mp->last_block;
if (info != 0x38)
{
query_status (s);
/*SIM*/ while ((info & 0x28) != 0x28)
{
pixma_sleep (10000);
error = request_image_block2 (s, &info);
if (s->cancel)
return -ECANCELED;
if (error < 0)
return error;
}
}
mp->needs_abort = (info != 0x38);
mp->state = state_finished;
return 0;
}
check_status (s);
/*SIM*/ while (handle_interrupt (s, 1) > 0);
/*SIM*/ block_size = IMAGE_BLOCK_SIZE;
error = request_image_block (s, &block_size, &info);
if (error < 0)
return error;
mp->last_block = info;
if (info != 0 && info != 0x38)
{
PDBG (pixma_dbg (1, "WARNING: Unknown info byte %x\n", info));
}
if (block_size == 0)
{
/* no image data at this moment. */
pixma_sleep (10000);
}
}
while (block_size == 0);
error = read_image_block (s, mp->rawimg + mp->rawimg_left);
if (error < 0)
{
mp->state = state_transfering;
return error;
}
bytes_received = error;
PASSERT (bytes_received == block_size);
/* TODO: simplify! */
mp->rawimg_left += bytes_received;
n = mp->rawimg_left / 3;
/* n = number of pixels in the buffer */
shift_rgb (mp->rawimg, n, shift[0], shift[1], shift[2],
mp->img + mp->imgbuf_ofs);
n *= 3;
mp->shifted_bytes += n;
mp->rawimg_left -= n; /* rawimg_left = 0, 1 or 2 bytes left in the buffer. */
mp->last_block_size = n;
memcpy (mp->rawimg, mp->rawimg + n, mp->rawimg_left);
}
while (mp->shifted_bytes <= 0);
if ((unsigned) mp->shifted_bytes < mp->last_block_size)
ib->rptr = mp->img + mp->last_block_size - mp->shifted_bytes;
else
ib->rptr = mp->img;
ib->rend = mp->img + mp->last_block_size;
return ib->rend - ib->rptr;
}
static void
mp750_finish_scan (pixma_t * s)
{
int error;
mp750_t *mp = (mp750_t *) s->subdriver;
switch (mp->state)
{
case state_transfering:
drain_bulk_in (s);
/* fall through */
case state_scanning:
case state_warmup:
error = abort_session (s);
if (error < 0)
{
PDBG (pixma_dbg (1, "WARNING:abort_session() failed: %s\n",
strerror (-error)));
reset_scanner (s);
}
/* fall through */
case state_finished:
query_status (s);
/*SIM*/ activate (s, 0);
if (mp->needs_abort)
{
mp->needs_abort = 0;
abort_session (s);
}
free (mp->buf);
mp->buf = mp->rawimg = NULL;
mp->state = state_idle;
/* fall through */
case state_idle:
break;
}
}
static void
mp750_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 const pixma_scan_ops_t pixma_mp750_ops = {
mp750_open,
mp750_close,
mp750_scan,
mp750_fill_buffer,
mp750_finish_scan,
mp750_wait_event,
mp750_check_param
};
/* FIXME: What is the maximum resolution? 2400 DPI?*/
#define DEVICE(name, pid, dpi, cap) { \
name, /* name */ \
0x04a9, pid, /* vid pid */ \
0, /* iface */ \
&pixma_mp750_ops, /* ops */ \
dpi, 2*(dpi), /* xdpi, ydpi */ \
638, 877, /* width, height */ \
PIXMA_CAP_ADF|cap \
}
const pixma_config_t pixma_mp750_devices[] = {
DEVICE ("Canon PIXMA MP750", 0x1706, 2400, PIXMA_CAP_EXPERIMENT),
DEVICE ("Canon PIXMA MP780", 0x1707, 2400, PIXMA_CAP_EXPERIMENT),
DEVICE ("Canon PIXMA MP760", 0x1708, 2400, PIXMA_CAP_EXPERIMENT),
DEVICE (NULL, 0, 0, 0)
};

Wyświetl plik

@ -0,0 +1,101 @@
/* SANE - Scanner Access Now Easy.
Copyright (C) 2006 Wittawat Yamwong <wittawat@web.de>
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.
*/
#ifndef PIXMA_RENAME_H
#define PIXMA_RENAME_H
#undef BACKEND_NAME
#define BACKEND_NAME pixma
#define pixma_cancel sanei_pixma_cancel
#define pixma_check_dpi sanei_pixma_check_dpi
#define pixma_check_result sanei_pixma_check_result
#define pixma_check_scan_param sanei_pixma_check_scan_param
#define pixma_cleanup sanei_pixma_cleanup
#define pixma_close sanei_pixma_close
#define pixma_cmd_transaction sanei_pixma_cmd_transaction
#define pixma_collect_devices sanei_pixma_collect_devices
#define pixma_connect sanei_pixma_connect
#define pixma_dbg DBG
#define pixma_disconnect sanei_pixma_disconnect
#define pixma_dump sanei_pixma_dump
#define pixma_enable_background sanei_pixma_enable_background
#define pixma_exec sanei_pixma_exec
#define pixma_exec_short_cmd sanei_pixma_exec_short_cmd
#define pixma_fill_gamma_table sanei_pixma_fill_gamma_table
#define pixma_find_scanners sanei_pixma_find_scanners
#define pixma_get_be16 sanei_pixma_get_be16
#define pixma_get_be32 sanei_pixma_get_be32
#define pixma_get_config sanei_pixma_get_config
#define pixma_get_device_config sanei_pixma_get_device_config
#define pixma_get_device_id sanei_pixma_get_device_id
#define pixma_get_device_model sanei_pixma_get_device_model
#define pixma_get_string sanei_pixma_get_string
#define pixma_get_time sanei_pixma_get_time
#define pixma_hexdump sanei_pixma_hexdump
#define pixma_init sanei_pixma_init
#define pixma_io_cleanup sanei_pixma_io_cleanup
#define pixma_io_init sanei_pixma_io_init
#define pixma_map_status_errno sanei_pixma_map_status_errno
#define pixma_mp150_devices sanei_pixma_mp150_devices
#define pixma_mp730_devices sanei_pixma_mp730_devices
#define pixma_mp750_devices sanei_pixma_mp750_devices
#define pixma_newcmd sanei_pixma_newcmd
#define pixma_open sanei_pixma_open
#define pixma_print_supported_devices sanei_pixma_print_supported_devices
#define pixma_read_image sanei_pixma_read_image
#define pixma_read sanei_pixma_read
#define pixma_reset_device sanei_pixma_reset_device
#define pixma_scan sanei_pixma_scan
#define pixma_set_be16 sanei_pixma_set_be16
#define pixma_set_be32 sanei_pixma_set_be32
#define pixma_set_debug_level sanei_pixma_set_debug_level
#define pixma_set_interrupt_mode sanei_pixma_set_interrupt_mode
#define pixma_sleep sanei_pixma_sleep
#define pixma_sum_bytes sanei_pixma_sum_bytes
#define pixma_wait_event sanei_pixma_wait_event
#define pixma_wait_interrupt sanei_pixma_wait_interrupt
#define pixma_write sanei_pixma_write
#endif

Wyświetl plik

@ -0,0 +1,255 @@
/* Automatically generated from pixma_sane.c */
static const SANE_Range constraint_gamma_table = { 0, 255, 0 };
static int
find_string_in_list (SANE_String_Const str, const SANE_String_Const * list)
{
int i;
for (i = 0; list[i] && strcmp (str, list[i]) != 0; i++)
{
}
return i;
}
static int
build_option_descriptors (struct pixma_sane_t *ss)
{
SANE_Option_Descriptor *sod;
option_descriptor_t *opt;
memset (OPT_IN_CTX, 0, sizeof (OPT_IN_CTX));
opt = &(OPT_IN_CTX[opt_opt_num_opts]);
sod = &opt->sod;
sod->type = SANE_TYPE_INT;
sod->title = SANE_TITLE_NUM_OPTIONS;
sod->desc = SANE_DESC_NUM_OPTIONS;
sod->name = "";
sod->unit = SANE_UNIT_NONE;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_DETECT;
sod->constraint_type = SANE_CONSTRAINT_NONE;
OPT_IN_CTX[opt_opt_num_opts].info = 0;
opt->def.w = opt_last;
opt->val.w = opt_last;
opt = &(OPT_IN_CTX[opt__group_1]);
sod = &opt->sod;
sod->type = SANE_TYPE_GROUP;
sod->title = SANE_I18N ("Scan mode");
sod->desc = sod->title;
opt = &(OPT_IN_CTX[opt_resolution]);
sod = &opt->sod;
sod->type = SANE_TYPE_INT;
sod->title = SANE_TITLE_SCAN_RESOLUTION;
sod->desc = SANE_DESC_SCAN_RESOLUTION;
sod->name = "resolution";
sod->unit = SANE_UNIT_DPI;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
sod->constraint_type = SANE_CONSTRAINT_WORD_LIST;
sod->constraint.word_list = ss->dpi_list;
OPT_IN_CTX[opt_resolution].info = SANE_INFO_RELOAD_PARAMS;
opt->def.w = 75;
opt->val.w = 75;
opt = &(OPT_IN_CTX[opt_mode]);
sod = &opt->sod;
sod->type = SANE_TYPE_STRING;
sod->title = SANE_TITLE_SCAN_MODE;
sod->desc = SANE_DESC_SCAN_MODE;
sod->name = "mode";
sod->unit = SANE_UNIT_NONE;
sod->size = 11;
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
sod->constraint_type = SANE_CONSTRAINT_STRING_LIST;
sod->constraint.string_list = ss->mode_list;
OPT_IN_CTX[opt_mode].info = SANE_INFO_RELOAD_PARAMS;
opt->def.s = "Color";
opt->val.w = find_string_in_list (opt->def.s, sod->constraint.string_list);
opt = &(OPT_IN_CTX[opt_source]);
sod = &opt->sod;
sod->type = SANE_TYPE_STRING;
sod->title = SANE_TITLE_SCAN_SOURCE;
sod->desc = SANE_DESC_SCAN_SOURCE;
sod->name = "source";
sod->unit = SANE_UNIT_NONE;
sod->size = 31;
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
sod->constraint_type = SANE_CONSTRAINT_STRING_LIST;
sod->constraint.string_list = ss->source_list;
OPT_IN_CTX[opt_source].info = 0;
opt->def.s = "Flatbed";
opt->val.w = find_string_in_list (opt->def.s, sod->constraint.string_list);
opt = &(OPT_IN_CTX[opt_button_controlled]);
sod = &opt->sod;
sod->type = SANE_TYPE_BOOL;
sod->title = SANE_I18N ("Button-controlled scan (experimental)");
sod->desc =
SANE_I18N
("When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button.");
sod->name = "button-controlled";
sod->unit = SANE_UNIT_NONE;
sod->size = sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE;
sod->constraint_type = SANE_CONSTRAINT_NONE;
OPT_IN_CTX[opt_button_controlled].info = 0;
opt->def.w = SANE_FALSE;
opt->val.w = SANE_FALSE;
opt = &(OPT_IN_CTX[opt__group_2]);
sod = &opt->sod;
sod->type = SANE_TYPE_GROUP;
sod->title = SANE_I18N ("Gamma");
sod->desc = sod->title;
opt = &(OPT_IN_CTX[opt_custom_gamma]);
sod = &opt->sod;
sod->type = SANE_TYPE_BOOL;
sod->title = SANE_TITLE_CUSTOM_GAMMA;
sod->desc = SANE_DESC_CUSTOM_GAMMA;
sod->name = "custom-gamma";
sod->unit = SANE_UNIT_NONE;
sod->size = sizeof (SANE_Word);
sod->cap =
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC |
SANE_CAP_INACTIVE;
sod->constraint_type = SANE_CONSTRAINT_NONE;
OPT_IN_CTX[opt_custom_gamma].info = 0;
opt->def.w = SANE_TRUE;
opt->val.w = SANE_TRUE;
opt = &(OPT_IN_CTX[opt_gamma_table]);
sod = &opt->sod;
sod->type = SANE_TYPE_INT;
sod->title = SANE_TITLE_GAMMA_VECTOR;
sod->desc = SANE_DESC_GAMMA_VECTOR;
sod->name = "gamma-table";
sod->unit = SANE_UNIT_NONE;
sod->size = 4096 * sizeof (SANE_Word);
sod->cap =
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC |
SANE_CAP_INACTIVE;
sod->constraint_type = SANE_CONSTRAINT_RANGE;
sod->constraint.range = &constraint_gamma_table;
OPT_IN_CTX[opt_gamma_table].info = 0;
opt = &(OPT_IN_CTX[opt__group_3]);
sod = &opt->sod;
sod->type = SANE_TYPE_GROUP;
sod->title = SANE_I18N ("Geometry");
sod->desc = sod->title;
opt = &(OPT_IN_CTX[opt_tl_x]);
sod = &opt->sod;
sod->type = SANE_TYPE_FIXED;
sod->title = SANE_TITLE_SCAN_TL_X;
sod->desc = SANE_DESC_SCAN_TL_X;
sod->name = "tl-x";
sod->unit = SANE_UNIT_MM;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
sod->constraint_type = SANE_CONSTRAINT_RANGE;
sod->constraint.range = &ss->xrange;
OPT_IN_CTX[opt_tl_x].info = SANE_INFO_RELOAD_PARAMS;
opt->def.w = SANE_FIX (0);
opt->val.w = SANE_FIX (0);
opt = &(OPT_IN_CTX[opt_tl_y]);
sod = &opt->sod;
sod->type = SANE_TYPE_FIXED;
sod->title = SANE_TITLE_SCAN_TL_Y;
sod->desc = SANE_DESC_SCAN_TL_Y;
sod->name = "tl-y";
sod->unit = SANE_UNIT_MM;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
sod->constraint_type = SANE_CONSTRAINT_RANGE;
sod->constraint.range = &ss->yrange;
OPT_IN_CTX[opt_tl_y].info = SANE_INFO_RELOAD_PARAMS;
opt->def.w = SANE_FIX (0);
opt->val.w = SANE_FIX (0);
opt = &(OPT_IN_CTX[opt_br_x]);
sod = &opt->sod;
sod->type = SANE_TYPE_FIXED;
sod->title = SANE_TITLE_SCAN_BR_X;
sod->desc = SANE_DESC_SCAN_BR_X;
sod->name = "br-x";
sod->unit = SANE_UNIT_MM;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
sod->constraint_type = SANE_CONSTRAINT_RANGE;
sod->constraint.range = &ss->xrange;
OPT_IN_CTX[opt_br_x].info = SANE_INFO_RELOAD_PARAMS;
opt->def.w = sod->constraint.range->max;
opt->val.w = sod->constraint.range->max;
opt = &(OPT_IN_CTX[opt_br_y]);
sod = &opt->sod;
sod->type = SANE_TYPE_FIXED;
sod->title = SANE_TITLE_SCAN_BR_Y;
sod->desc = SANE_DESC_SCAN_BR_Y;
sod->name = "br-y";
sod->unit = SANE_UNIT_MM;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
sod->constraint_type = SANE_CONSTRAINT_RANGE;
sod->constraint.range = &ss->yrange;
OPT_IN_CTX[opt_br_y].info = SANE_INFO_RELOAD_PARAMS;
opt->def.w = sod->constraint.range->max;
opt->val.w = sod->constraint.range->max;
opt = &(OPT_IN_CTX[opt__group_4]);
sod = &opt->sod;
sod->type = SANE_TYPE_GROUP;
sod->title = SANE_I18N ("Buttons");
sod->desc = sod->title;
opt = &(OPT_IN_CTX[opt_button_update]);
sod = &opt->sod;
sod->type = SANE_TYPE_BUTTON;
sod->title = SANE_I18N ("Update button state");
sod->desc = sod->title;
sod->name = "button-update";
sod->unit = SANE_UNIT_NONE;
sod->size = 0;
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
sod->constraint_type = SANE_CONSTRAINT_NONE;
OPT_IN_CTX[opt_button_update].info = 0;
opt = &(OPT_IN_CTX[opt_button_1]);
sod = &opt->sod;
sod->type = SANE_TYPE_INT;
sod->title = SANE_I18N ("Button 1");
sod->desc = sod->title;
sod->name = "button-1";
sod->unit = SANE_UNIT_NONE;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
sod->constraint_type = SANE_CONSTRAINT_NONE;
OPT_IN_CTX[opt_button_1].info = 0;
opt->def.w = 0;
opt->val.w = 0;
opt = &(OPT_IN_CTX[opt_button_2]);
sod = &opt->sod;
sod->type = SANE_TYPE_INT;
sod->title = SANE_I18N ("Button 2");
sod->desc = sod->title;
sod->name = "button-2";
sod->unit = SANE_UNIT_NONE;
sod->size = 1 * sizeof (SANE_Word);
sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
sod->constraint_type = SANE_CONSTRAINT_NONE;
OPT_IN_CTX[opt_button_2].info = 0;
opt->def.w = 0;
opt->val.w = 0;
return 0;
}

Wyświetl plik

@ -0,0 +1,44 @@
/* Automatically generated from pixma_sane.c */
typedef union
{
SANE_Word w;
SANE_Int i;
SANE_Bool b;
SANE_Fixed f;
SANE_String s;
void *ptr;
} option_value_t;
typedef enum
{
opt_opt_num_opts,
opt__group_1,
opt_resolution,
opt_mode,
opt_source,
opt_button_controlled,
opt__group_2,
opt_custom_gamma,
opt_gamma_table,
opt__group_3,
opt_tl_x,
opt_tl_y,
opt_br_x,
opt_br_y,
opt__group_4,
opt_button_update,
opt_button_1,
opt_button_2,
opt_last
} option_t;
typedef struct
{
SANE_Option_Descriptor sod;
option_value_t val, def;
SANE_Word info;
} option_descriptor_t;
struct pixma_sane_t;
static int build_option_descriptors (struct pixma_sane_t *ss);

2
configure vendored
Wyświetl plik

@ -26804,7 +26804,7 @@ echo "$as_me: Manually selected backends: ${BACKENDS}" >&6;}
BACKENDS="abaton agfafocus apple artec as6e avision bh canon \
canon630u coolscan coolscan2 dc25 dmc \
epson fujitsu genesys gt68xx hp leo lexmark matsushita microtek \
microtek2 mustek mustek_usb nec pie plustek \
microtek2 mustek mustek_usb nec pie pixma plustek \
plustek_pp ricoh s9036 sceptre sharp \
sp15c st400 tamarack test teco1 teco2 teco3 umax umax_pp umax1220u \
artec_eplus48u ma1509 ibm hp5400 u12 snapscan niash sm3840 hp4200 \

Wyświetl plik

@ -338,7 +338,7 @@ else
BACKENDS="abaton agfafocus apple artec as6e avision bh canon \
canon630u coolscan coolscan2 dc25 dmc \
epson fujitsu genesys gt68xx hp leo lexmark matsushita microtek \
microtek2 mustek mustek_usb nec pie plustek \
microtek2 mustek mustek_usb nec pie pixma plustek \
plustek_pp ricoh s9036 sceptre sharp \
sp15c st400 tamarack test teco1 teco2 teco3 umax umax_pp umax1220u \
artec_eplus48u ma1509 ibm hp5400 u12 snapscan niash sm3840 hp4200 \

Wyświetl plik

@ -53,7 +53,7 @@ SECT5 = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 sane-dll.5 \
sane-coolscan2.5 sane-hpsj5s.5 sane-gt68xx.5 sane-artec_eplus48u.5 \
sane-ma1509.5 sane-ibm.5 sane-hp5400.5 sane-plustek_pp.5 sane-u12.5 \
sane-niash.5 sane-sm3840.5 sane-genesys.5 sane-hp4200.5 \
sane-mustek_usb2.5 sane-hp3500.5
sane-mustek_usb2.5 sane-hp3500.5 sane-pixma.5
SECT7 = sane.7
SECT8 = saned.8
MANPAGES = $(SECT1) $(SECT5) $(SECT7) $(SECT8)
@ -106,7 +106,7 @@ DISTFILES = Makefile.in backend-writing.txt descriptions.txt \
sane-hpsj5s.man gamma4scanimage.man sane-gt68xx.man sane-artec_eplus48u.man \
sane-ma1509.man sane-ibm.man sane-hp5400.man sane-plustek_pp.man \
sane-u12.man sane-niash.man sane-sm3840.man sane-genesys.man sane-hp4200.man \
sane-mustek_usb2.man sane-hp3500.man
sane-mustek_usb2.man sane-hp3500.man sane-pixma.man
.PHONY: all clean depend dist distclean html html-man install \
sane-html uninstall

Wyświetl plik

@ -11,10 +11,10 @@
; See doc/descriptions.txt for details.
:backend "pixma" ; name of backend
:version "0.11.0" ; version of backend (or "unmaintained")
;:new :yes ; Is the backend new to this SANE release?
:version "0.11.2" ; version of backend (or "unmaintained")
:new :yes ; Is the backend new to this SANE release?
; :yes or :no
;:manpage "" ; name of manpage (if it exists)
:manpage "sane-pixma" ; name of manpage (if it exists)
:url "http://home.arcor.de/wittawat/pixma/" ; backend's web page
:devicetype :scanner ; start of a list of devices....
@ -45,10 +45,17 @@
:usbid "0x04a9" "0x170c"
:status :basic
:model "imageCLASS MP730"
:model "MultiPASS MP700"
:interface "USB"
:usbid "0x04a9" "0x2630"
:status :minimal
:comment "Is it the same model as SmartBase MP700 Photo sold in Germany?"
:model "MultiPASS MP730"
:interface "USB"
:usbid "0x04a9" "0x262f"
:status :minimal
:comment "Is it the same model as SmartBase MP730 Photo sold in Germany?"
:model "PIXMA MP750"
:interface "USB"
@ -64,10 +71,15 @@
:interface "USB"
:usbid "0x04a9" "0x1707"
:status :minimal
:comment "Scanner hangs when cancel while it scans."
:comment "Scanner hangs if cancel while it scans."
:model "PIXMA MP800"
:interface "USB"
:usbid "0x04a9" "0x170d"
:status :untested
:comment "Should work but is untested."
:model "PIXMA MP830"
:interface "USB"
:usbid "0x04a9" "0x1713"
:status :minimal

112
doc/sane-pixma.man 100644
Wyświetl plik

@ -0,0 +1,112 @@
.TH "sane-pixma" "5" "26 May 2006" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy"
.IX sane-pixma
.SH NAME
sane-pixma \- SANE backend for Canon PIXMA MP series
.SH DESCRIPTION
The
.B sane-pixma
library implements a SANE (Scanner Access Now Easy) backend that provides
access to Canon PIXMA multi-function devices (All-in-one printers).
Currently, the following models work with this backend:
.PP
.RS
PIXMA MP150, PIXMA MP170, PIXMA MP450, PIXMA MP500
.RE
.PP
The following models are marked as experimental because they are not well
tested and/or the scanner sometimes hangs and must be switched off and on.
Therefore they are disabled by default. (See PIXMA_EXPERIMENT below)
.PP
.RS
MultiPASS MP700, MultiPASS MP730,
.br
PIXMA MP750, PIXMA MP760, PIXMA MP780,
.br
PIXMA MP800, PIXMA MP830
.RE
.PP
The backend supports
.br
* resolutions 75,150,300,600,1200 and 2400 DPI,
.br
* color and grayscale mode,
.br
* a custom gamma table with 4096 entries and
.br
* automatic document feeder.
.PP
The device name is in the form
.BI pixma: xxxxyyyy_zzzzz
where x, y and z are vendor ID, product ID and serial number respectively.
Example: pixma:04A91709_123456 is a MP150.
.PP
This backend is in
.B alpha
stage and will stay in this stage until we get the programming manual
for the hardware. Although we have tested it as good as we could, it will
not work in every situations. You will find an up-to-date status at the
project homepage. (See below)
.SH FILES
.TP
.I @LIBDIR@/libsane-pixma.a
The static library implementing this backend.
.TP
.I @LIBDIR@/libsane-pixma.so
The shared library implementing this backend (present on systems that
support dynamic loading).
.SH ENVIRONMENT
.TP
.B SANE_DEBUG_PIXMA
If the library was compiled with debug support enabled, this environment
variable controls the debug level for this backend. Higher value increases
the verbosity.
.PP
.RS
0 print nothing
.br
1 print error and warning messages
.br
2 print informative messages
.br
3 print debugging messages
.br
10 dump USB traffics
.br
.RE
.TP
.B PIXMA_EXPERIMENT
Setting to a non-zero value will enable the support for experimental models.
You should also set SANE_DEBUG_PIXMA to 10 and keep the last log file
somewhere for the case that the backend fails.
.SH "SEE ALSO"
.BR sane (7),
.BR sane-dll (5),
.I http://home.arcor.de/wittawat/pixma/
.SH AUTHOR
Wittawat Yamwong
.PP
I would like to thank:
.br
Farvil for testing MP150
.br
Jose Juan Iglesias for testing MP450
.br
Malcolm Caldwell for testing MP170
.br
Martin Schewe for setting up mailing list for discussion
.br
Rune Kock for testing MP500
.br
Ryan McDonald for testing MP830
.br
Stephan Aegerter for testing MP780
.br
Trevor Hobson for testing MP730
.br
Wade Fitzpatrick for testing MP730
.br

Wyświetl plik

@ -365,6 +365,13 @@ SCSI flatbed scanners. See
.BR sane-pie (5)
for details.
.TP
.B pixma
The pixma backend supports Canon PIXMA MP series (multi-function devices). See
.BR sane-pixma (5)
or
.I http://home.arcor.de/wittawat/pixma/
for details.
.TP
.B plustek
The SANE plustek backend supports USB flatbed scanners that use the National
Semiconductor LM983[1/2/3]-chipset aka Merlin. Scanners using this LM983x chips