kopia lustrzana https://gitlab.com/sane-project/backends
Added pixma backend.
Moved doc/descriptions-external/pixma.desc to doc/descriptionsmerge-requests/1/head
rodzic
a1061d821f
commit
258b7934af
2
AUTHORS
2
AUTHORS
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -43,6 +43,7 @@ nec
|
|||
niash
|
||||
pie
|
||||
pint
|
||||
pixma
|
||||
plustek
|
||||
#plustek_pp
|
||||
#pnm
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue