sane-project-backends/backend/pixma.c

1538 wiersze
38 KiB
C

/* SANE - Scanner Access Now Easy.
Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
Copyright (C) 2006-2007 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 <errno.h>
#include <string.h>
#include <stdlib.h>
#ifdef USE_PTHREAD
# include <pthread.h>
#endif
#include <signal.h> /* sigaction(POSIX) */
#include <unistd.h> /* POSIX: write read close pipe */
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include "pixma_rename.h"
#include "pixma.h"
# define DEBUG_NOT_STATIC
# include "../include/sane/sane.h"
# include "../include/sane/sanei.h"
# include "../include/sane/saneopts.h"
# include "../include/sane/sanei_thread.h"
# include "../include/sane/sanei_backend.h"
# include "../include/sane/sanei_config.h"
#ifdef NDEBUG
# define PDBG(x)
#else
# define PDBG(x) IF_DBG(x)
#endif /* NDEBUG */
#ifdef __GNUC__
# define UNUSED(v) (void) v
#else
# define UNUSED(v)
#endif
#define DECL_CTX pixma_sane_t *ss = check_handle(h)
#define OPT_IN_CTX ss->opt
#define SOD(opt) OPT_IN_CTX[opt].sod
#define OVAL(opt) OPT_IN_CTX[opt].val
#define AUTO_GAMMA 2.2
#include "pixma_sane_options.h" /* generated by gen_options.py */
typedef struct pixma_sane_t
{
struct pixma_sane_t *next;
pixma_t *s;
pixma_scan_param_t sp;
SANE_Bool cancel;
/* valid states: idle, !idle && scanning, !idle && !scanning */
SANE_Bool idle;
SANE_Bool scanning;
SANE_Status last_read_status; /* valid if !idle && !scanning */
option_descriptor_t opt[opt_last];
SANE_Range xrange, yrange;
SANE_Word dpi_list[9]; /* up to 9600 dpi */
SANE_String_Const mode_list[3];
uint8_t gamma_table[4096];
SANE_String_Const source_list[4];
pixma_paper_source_t source_map[4];
unsigned byte_pos_in_line, output_line_size;
unsigned image_bytes_read;
unsigned page_count; /* valid for ADF */
SANE_Pid reader_taskid;
int wpipe, rpipe;
SANE_Bool reader_stop;
} pixma_sane_t;
static const char vendor_str[] = "CANON";
static const char type_str[] = "multi-function peripheral";
static pixma_sane_t *first_scanner = NULL;
static const SANE_Device **dev_list = NULL;
static const char* conf_devices[MAX_CONF_DEVICES];
static SANE_Status config_attach_pixma(SANEI_Config * config, const char *devname)
{
int i;
UNUSED(config);
for (i=0; i < (MAX_CONF_DEVICES -1); i++)
{
if(conf_devices[i] == NULL)
{
conf_devices[i] = strdup(devname);
return SANE_STATUS_GOOD;
}
}
return SANE_STATUS_INVAL;
}
static SANE_Status
map_error (int error)
{
if (error >= 0)
return SANE_STATUS_GOOD;
switch (error)
{
case PIXMA_ENOMEM:
return SANE_STATUS_NO_MEM;
case PIXMA_ECANCELED:
return SANE_STATUS_CANCELLED;
case PIXMA_EBUSY:
return SANE_STATUS_DEVICE_BUSY;
case PIXMA_EINVAL:
return SANE_STATUS_INVAL;
case PIXMA_EACCES:
return SANE_STATUS_ACCESS_DENIED;
case PIXMA_EPAPER_JAMMED:
return SANE_STATUS_JAMMED;
case PIXMA_ENO_PAPER:
return SANE_STATUS_NO_DOCS;
case PIXMA_ECOVER_OPEN:
return SANE_STATUS_COVER_OPEN;
case PIXMA_ENOTSUP:
return SANE_STATUS_UNSUPPORTED;
case PIXMA_EPROTO:
case PIXMA_ENODEV:
case PIXMA_EIO:
case PIXMA_ETIMEDOUT:
return SANE_STATUS_IO_ERROR;
}
PDBG (pixma_dbg (1, "BUG: unmapped error %d\n", error));
return SANE_STATUS_IO_ERROR;
}
static int
getenv_atoi (const char *name, int def)
{
const char *str = getenv (name);
return (str) ? atoi (str) : def;
}
#define CONST_CAST(t,x) (t)(x)
static void
free_block (const void * ptr)
{
free (CONST_CAST (void *, ptr));
}
static void
cleanup_device_list (void)
{
if (dev_list)
{
int i;
for (i = 0; dev_list[i]; i++)
{
free_block ((const void *) dev_list[i]->name);
free_block ((const void *) dev_list[i]->model);
free_block ((const void *) dev_list[i]);
}
}
free (dev_list);
dev_list = NULL;
}
static void
find_scanners (void)
{
unsigned i, nscanners;
cleanup_device_list ();
nscanners = pixma_find_scanners (conf_devices);
PDBG (pixma_dbg (3, "pixma_find_scanners() found %u devices\n", nscanners));
dev_list =
(const SANE_Device **) calloc (nscanners + 1, sizeof (*dev_list));
if (!dev_list)
return;
for (i = 0; i != nscanners; i++)
{
SANE_Device *sdev = (SANE_Device *) calloc (1, sizeof (*sdev));
char *name, *model;
if (!sdev)
goto nomem;
name = strdup (pixma_get_device_id (i));
model = strdup (pixma_get_device_model (i));
if (!name || !model)
{
free (name);
free (model);
free (sdev);
goto nomem;
}
sdev->name = name;
sdev->model = model;
sdev->vendor = vendor_str;
sdev->type = type_str;
dev_list[i] = sdev;
}
/* dev_list is already NULL terminated by calloc(). */
return;
nomem:
PDBG (pixma_dbg (1, "WARNING:not enough memory for device list\n"));
return;
}
static pixma_sane_t *
check_handle (SANE_Handle h)
{
pixma_sane_t *p;
for (p = first_scanner; p && (SANE_Handle) p != h; p = p->next)
{
}
return p;
}
static void
update_button_state (pixma_sane_t * ss, SANE_Int * info)
{
SANE_Int b1 = OVAL (opt_button_1).w;
SANE_Int b2 = OVAL (opt_button_2).w;
uint32_t ev = pixma_wait_event (ss->s, 300);
switch (ev & ~PIXMA_EV_ACTION_MASK)
{
case PIXMA_EV_BUTTON1:
b1 = (ev & PIXMA_EV_ACTION_MASK) + 1;
break;
case PIXMA_EV_BUTTON2:
b2 = (ev & PIXMA_EV_ACTION_MASK) + 1;
break;
}
if (b1 != OVAL (opt_button_1).w || b2 != OVAL (opt_button_2).w)
*info |= SANE_INFO_RELOAD_OPTIONS;
OVAL (opt_button_1).w = b1;
OVAL (opt_button_2).w = b2;
}
static SANE_Bool
enable_option (pixma_sane_t * ss, SANE_Int o, SANE_Bool enable)
{
SANE_Word save = SOD (o).cap;
if (enable)
SOD (o).cap &= ~SANE_CAP_INACTIVE;
else
SOD (o).cap |= SANE_CAP_INACTIVE;
return (save != SOD (o).cap);
}
static void
clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info)
{
SANE_Option_Descriptor *sod = &SOD (n);
SANE_Word *va = (SANE_Word *) v;
const SANE_Range *range = sod->constraint.range;
int i, nmemb;
nmemb = sod->size / sizeof (SANE_Word);
for (i = 0; i < nmemb; i++)
{
SANE_Word value = va[i];
if (value < range->min)
{
value = range->min;
}
else if (value > range->max)
{
value = range->max;
}
if (range->quant != 0)
{
value = (value - range->min + range->quant / 2) /
range->quant * range->quant;
}
if (value != va[i])
{
va[i] = value;
*info |= SANE_INFO_INEXACT;
}
}
}
static void
select_value_from_list (pixma_sane_t * ss, SANE_Int n, void *v,
SANE_Int * info)
{
SANE_Option_Descriptor *sod = &SOD (n);
SANE_Word *va = (SANE_Word *) v;
const SANE_Word *list = sod->constraint.word_list;
int i, j, nmemb;
nmemb = sod->size / sizeof (SANE_Word);
for (i = 0; i < nmemb; i++)
{
SANE_Word value = va[i];
SANE_Word mindelta = abs (value - list[1]);
SANE_Word nearest = list[1];
for (j = 2; j <= list[0]; j++)
{
SANE_Word delta = abs (value - list[j]);
if (delta < mindelta)
{
mindelta = delta;
nearest = list[j];
}
if (mindelta == 0)
break;
}
if (va[i] != nearest)
{
va[i] = nearest;
*info |= SANE_INFO_INEXACT;
}
}
}
static SANE_Status
control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v,
SANE_Int * info)
{
option_descriptor_t *opt = &(OPT_IN_CTX[n]);
SANE_Word val;
switch (a)
{
case SANE_ACTION_GET_VALUE:
switch (opt->sod.type)
{
case SANE_TYPE_BOOL:
case SANE_TYPE_INT:
case SANE_TYPE_FIXED:
*(SANE_Word *) v = opt->val.w;
break;
default:
return SANE_STATUS_UNSUPPORTED;
}
return SANE_STATUS_GOOD;
case SANE_ACTION_SET_VALUE:
switch (opt->sod.type)
{
case SANE_TYPE_BOOL:
val = *(SANE_Word *) v;
if (val != SANE_TRUE && val != SANE_FALSE)
return SANE_STATUS_INVAL;
opt->val.w = val;
break;
case SANE_TYPE_INT:
case SANE_TYPE_FIXED:
if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE)
clamp_value (ss, n, v, info);
else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST)
select_value_from_list (ss, n, v, info);
opt->val.w = *(SANE_Word *) v;
break;
default:
return SANE_STATUS_UNSUPPORTED;
}
*info |= opt->info;
return SANE_STATUS_GOOD;
case SANE_ACTION_SET_AUTO:
switch (opt->sod.type)
{
case SANE_TYPE_BOOL:
case SANE_TYPE_INT:
case SANE_TYPE_FIXED:
opt->val.w = opt->def.w;
break;
default:
return SANE_STATUS_UNSUPPORTED;
}
*info |= opt->info;
return SANE_STATUS_GOOD;
}
return SANE_STATUS_UNSUPPORTED;
}
static SANE_Status
control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v,
SANE_Int * info)
{
option_descriptor_t *opt = &(OPT_IN_CTX[n]);
const SANE_String_Const *slist = opt->sod.constraint.string_list;
SANE_String str = (SANE_String) v;
int i;
if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE)
{
switch (a)
{
case SANE_ACTION_GET_VALUE:
strcpy (str, opt->val.s);
break;
case SANE_ACTION_SET_AUTO:
str = opt->def.s;
/* fall through */
case SANE_ACTION_SET_VALUE:
strncpy (opt->val.s, str, opt->sod.size - 1);
*info |= opt->info;
break;
}
return SANE_STATUS_GOOD;
}
else
{
switch (a)
{
case SANE_ACTION_GET_VALUE:
strcpy (str, slist[opt->val.w]);
break;
case SANE_ACTION_SET_AUTO:
str = opt->def.ptr;
/* fall through */
case SANE_ACTION_SET_VALUE:
i = 0;
while (slist[i] && strcasecmp (str, slist[i]) != 0)
i++;
if (!slist[i])
return SANE_STATUS_INVAL;
if (strcmp (slist[i], str) != 0)
{
strcpy (str, slist[i]);
*info |= SANE_INFO_INEXACT;
}
opt->val.w = i;
*info |= opt->info;
break;
}
return SANE_STATUS_GOOD;
}
}
static SANE_Status
control_option (pixma_sane_t * ss, SANE_Int n,
SANE_Action a, void *v, SANE_Int * info)
{
int result, i;
result = SANE_STATUS_UNSUPPORTED;
switch (n)
{
case opt_gamma_table:
switch (a)
{
case SANE_ACTION_SET_VALUE:
clamp_value (ss, n, v, info);
for (i = 0; i != 4096; i++)
ss->gamma_table[i] = *((SANE_Int *) v + i);
break;
case SANE_ACTION_GET_VALUE:
for (i = 0; i != 4096; i++)
*((SANE_Int *) v + i) = ss->gamma_table[i];
break;
case SANE_ACTION_SET_AUTO:
pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table,
sizeof (ss->gamma_table));
break;
default:
return SANE_STATUS_UNSUPPORTED;
}
return SANE_STATUS_GOOD;
case opt_button_update:
if (a == SANE_ACTION_SET_VALUE)
{
update_button_state (ss, info);
return SANE_STATUS_GOOD;
}
else
{
return SANE_STATUS_INVAL;
}
break;
}
switch (SOD (n).type)
{
case SANE_TYPE_BOOL:
case SANE_TYPE_INT:
case SANE_TYPE_FIXED:
result = control_scalar_option (ss, n, a, v, info);
break;
case SANE_TYPE_STRING:
result = control_string_option (ss, n, a, v, info);
break;
case SANE_TYPE_BUTTON:
case SANE_TYPE_GROUP:
PDBG (pixma_dbg (1, "BUG:control_option():Unhandled option\n"));
result = SANE_STATUS_INVAL;
break;
}
if (result != SANE_STATUS_GOOD)
return result;
switch (n)
{
case opt_custom_gamma:
if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)
{
if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b))
*info |= SANE_INFO_RELOAD_OPTIONS;
}
break;
}
return result;
}
#ifndef NDEBUG
static void
print_scan_param (int level, const pixma_scan_param_t * sp)
{
pixma_dbg (level, "Scan parameters\n");
pixma_dbg (level, " line_size=%u image_size=%u channels=%u depth=%u\n",
sp->line_size, sp->image_size, sp->channels, sp->depth);
pixma_dbg (level, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n",
sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h);
pixma_dbg (level, " gamma_table=%p source=%d\n", sp->gamma_table,
sp->source);
}
#endif
static int
calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp)
{
int x1, y1, x2, y2;
int error;
memset (sp, 0, sizeof (*sp));
sp->channels = (OVAL (opt_mode).w == 0) ? 3 : 1;
sp->xdpi = sp->ydpi = OVAL (opt_resolution).w;
#define PIXEL(x,dpi) (int)((SANE_UNFIX(x) / 25.4 * (dpi)) + 0.5)
x1 = PIXEL (OVAL (opt_tl_x).w, sp->xdpi);
x2 = PIXEL (OVAL (opt_br_x).w, sp->xdpi);
if (x2 < x1)
{
int temp = x1;
x1 = x2;
x2 = temp;
}
y1 = PIXEL (OVAL (opt_tl_y).w, sp->ydpi);
y2 = PIXEL (OVAL (opt_br_y).w, sp->ydpi);
if (y2 < y1)
{
int temp = y1;
y1 = y2;
y2 = temp;
}
#undef PIXEL
sp->x = x1;
sp->y = y1;
sp->w = x2 - x1;
sp->h = y2 - y1;
if (sp->w == 0)
sp->w = 1;
if (sp->h == 0)
sp->h = 1;
sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL;
sp->source = ss->source_map[OVAL (opt_source).w];
sp->adf_pageid = ss->page_count;
error = pixma_check_scan_param (ss->s, sp);
if (error < 0)
{
PDBG (pixma_dbg (1, "BUG:calc_scan_param() failed %d\n", error));
PDBG (print_scan_param (1, sp));
}
return error;
}
static void
init_option_descriptors (pixma_sane_t * ss)
{
const pixma_config_t *cfg;
int i;
cfg = pixma_get_config (ss->s);
/* setup range for the scan area. */
ss->xrange.min = SANE_FIX (0);
ss->xrange.max = SANE_FIX (cfg->width / 75.0 * 25.4);
ss->xrange.quant = SANE_FIX (0);
ss->yrange.min = SANE_FIX (0);
ss->yrange.max = SANE_FIX (cfg->height / 75.0 * 25.4);
ss->yrange.quant = SANE_FIX (0);
/* setup dpi up to the value supported by the scanner. */
i = 0;
do
{
i++;
ss->dpi_list[i] = 75 * (1 << (i - 1)); /* 75 x 2^(i-1) */
}
while ((unsigned) ss->dpi_list[i] != cfg->xdpi);
ss->dpi_list[0] = i;
/* mode_list and source_list were already NULL-terminated,
* because the whole pixma_sane_t was cleared during allocation. */
/* setup available mode. */
ss->mode_list[0] = SANE_VALUE_SCAN_MODE_COLOR;
if (cfg->cap & PIXMA_CAP_GRAY)
{
ss->mode_list[1] = SANE_VALUE_SCAN_MODE_GRAY;
}
/* setup paper source */
i = 0;
ss->source_list[i] = SANE_I18N ("Flatbed");
ss->source_map[i] = PIXMA_SOURCE_FLATBED;
i++;
if (cfg->cap & PIXMA_CAP_ADF)
{
ss->source_list[i] = SANE_I18N ("Automatic Document Feeder");
ss->source_map[i] = PIXMA_SOURCE_ADF;
i++;
}
if ((cfg->cap & PIXMA_CAP_ADFDUP) == PIXMA_CAP_ADFDUP)
{
ss->source_list[i] = SANE_I18N ("ADF Duplex");
ss->source_map[i] = PIXMA_SOURCE_ADFDUP;
i++;
}
if (cfg->cap & PIXMA_CAP_TPU)
{
ss->source_list[i] = SANE_I18N ("Transparency Unit");
ss->source_map[i] = PIXMA_SOURCE_TPU;
i++;
}
build_option_descriptors (ss);
/* Enable options that are available only in some scanners. */
if (cfg->cap & PIXMA_CAP_GAMMA_TABLE)
{
enable_option (ss, opt_custom_gamma, SANE_TRUE);
sane_control_option (ss, opt_custom_gamma, SANE_ACTION_SET_AUTO,
NULL, NULL);
pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, 4096);
}
enable_option (ss, opt_button_controlled,
((cfg->cap & PIXMA_CAP_EVENTS) != 0));
}
/* Writing to reader_ss outside reader_process() is a BUG! */
static pixma_sane_t *reader_ss = NULL;
static RETSIGTYPE
reader_signal_handler (int sig)
{
if (reader_ss)
{
reader_ss->reader_stop = SANE_TRUE;
/* reader process is ended by SIGTERM, so no cancel in this case */
if (sig != SIGTERM)
pixma_cancel (reader_ss->s);
}
}
static int
write_all (pixma_sane_t * ss, void *buf_, size_t size)
{
uint8_t *buf = (uint8_t *) buf_;
int count;
while (size != 0 && !ss->reader_stop)
{
count = write (ss->wpipe, buf, size);
if (count == -1 && errno != EINTR)
break;
if (count == -1 && errno == EINTR)
continue;
buf += count;
size -= count;
}
return buf - (uint8_t *) buf_;
}
/* NOTE: reader_loop() runs either in a separate thread or process. */
static SANE_Status
reader_loop (pixma_sane_t * ss)
{
void *buf;
unsigned bufsize;
int count = 0;
PDBG (pixma_dbg (3, "Reader task started\n"));
/*bufsize = ss->sp.line_size + 1;*/ /* XXX: "odd" bufsize for testing pixma_read_image() */
bufsize = ss->sp.line_size; /* bufsize EVEN needed by Xsane for 48 bits depth */
buf = malloc (bufsize);
if (!buf)
{
count = PIXMA_ENOMEM;
goto done;
}
count = pixma_activate_connection (ss->s);
if (count < 0)
goto done;
pixma_enable_background (ss->s, 1);
if (OVAL (opt_button_controlled).b && ss->page_count == 0)
{
int start = 0;
#ifndef NDEBUG
pixma_dbg (1, "==== Button-controlled scan mode is enabled.\n");
pixma_dbg (1, "==== To proceed, press 'SCAN' or 'COLOR' button. "
"To cancel, press 'GRAY' button.\n");
#endif
while (pixma_wait_event (ss->s, 10) != 0)
{
}
while (!start)
{
uint32_t events;
if (ss->reader_stop)
{
count = PIXMA_ECANCELED;
goto done;
}
events = pixma_wait_event (ss->s, 1000);
switch (events & ~PIXMA_EV_ACTION_MASK)
{
case PIXMA_EV_BUTTON1:
start = 1;
break;
case PIXMA_EV_BUTTON2:
count = PIXMA_ECANCELED;
goto done;
}
}
}
count = pixma_scan (ss->s, &ss->sp);
if (count >= 0)
{
while ((count = pixma_read_image (ss->s, buf, bufsize)) > 0)
{
if (write_all (ss, buf, count) != count)
pixma_cancel (ss->s);
}
}
done:
pixma_enable_background (ss->s, 0);
pixma_deactivate_connection (ss->s);
free (buf);
close (ss->wpipe);
ss->wpipe = -1;
if (count >= 0)
{
PDBG (pixma_dbg (3, "Reader task terminated\n"));
}
else
{
PDBG (pixma_dbg
(2, "Reader task terminated: %s\n", pixma_strerror (count)));
}
return map_error (count);
}
static int
reader_process (void *arg)
{
pixma_sane_t *ss = (pixma_sane_t *) arg;
struct SIGACTION sa;
reader_ss = ss;
memset (&sa, 0, sizeof (sa));
sigemptyset (&sa.sa_mask);
sa.sa_handler = reader_signal_handler;
/* FIXME: which signal else? */
sigaction (SIGHUP, &sa, NULL);
sigaction (SIGINT, &sa, NULL);
sigaction (SIGPIPE, &sa, NULL);
sigaction (SIGTERM, &sa, NULL);
close (ss->rpipe);
ss->rpipe = -1;
return reader_loop (ss);
}
static int
reader_thread (void *arg)
{
pixma_sane_t *ss = (pixma_sane_t *) arg;
#ifdef USE_PTHREAD
/* Block SIGPIPE. We will handle this in reader_loop() by checking
ss->reader_stop and the return value from write(). */
sigset_t sigs;
sigemptyset (&sigs);
sigaddset (&sigs, SIGPIPE);
pthread_sigmask (SIG_BLOCK, &sigs, NULL);
#endif /* USE_PTHREAD */
return reader_loop (ss);
}
static SANE_Pid
terminate_reader_task (pixma_sane_t * ss, int *exit_code)
{
SANE_Pid result, pid;
int status = 0;
pid = ss->reader_taskid;
if (pid == -1)
return -1;
if (sanei_thread_is_forked ())
{
sanei_thread_kill (pid);
}
else
{
ss->reader_stop = SANE_TRUE;
/* pixma_cancel (ss->s); What is this for ? Makes end-of-scan buggy => removing */
}
result = sanei_thread_waitpid (pid, &status);
ss->reader_taskid = -1;
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
ss->idle = SANE_TRUE;
if (result == pid)
{
if (exit_code)
*exit_code = status;
return pid;
}
else
{
PDBG (pixma_dbg (1, "WARNING:waitpid() failed %s\n", strerror (errno)));
return -1;
}
}
static int
start_reader_task (pixma_sane_t * ss)
{
int fds[2];
SANE_Pid pid;
int is_forked;
if (ss->rpipe != -1 || ss->wpipe != -1)
{
PDBG (pixma_dbg
(1, "BUG:rpipe = %d, wpipe = %d\n", ss->rpipe, ss->wpipe));
close (ss->rpipe);
close (ss->wpipe);
ss->rpipe = -1;
ss->wpipe = -1;
}
if (ss->reader_taskid != -1)
{
PDBG (pixma_dbg
(1, "BUG:reader_taskid(%ld) != -1\n", (long) ss->reader_taskid));
terminate_reader_task (ss, NULL);
}
if (pipe (fds) == -1)
{
PDBG (pixma_dbg (1, "ERROR:start_reader_task():pipe() failed %s\n",
strerror (errno)));
return PIXMA_ENOMEM;
}
ss->rpipe = fds[0];
ss->wpipe = fds[1];
ss->reader_stop = SANE_FALSE;
is_forked = sanei_thread_is_forked ();
if (is_forked)
{
pid = sanei_thread_begin (reader_process, ss);
if (pid > 0)
{
close (ss->wpipe);
ss->wpipe = -1;
}
}
else
{
pid = sanei_thread_begin (reader_thread, ss);
}
if (pid == -1)
{
close (ss->wpipe);
close (ss->rpipe);
ss->wpipe = -1;
ss->rpipe = -1;
PDBG (pixma_dbg (1, "ERROR:unable to start reader task\n"));
return PIXMA_ENOMEM;
}
PDBG (pixma_dbg (3, "Reader task id=%ld (%s)\n", (long) pid,
(is_forked) ? "forked" : "threaded"));
ss->reader_taskid = pid;
return 0;
}
static SANE_Status
read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
{
int count, status;
if (readlen)
*readlen = 0;
if (ss->image_bytes_read >= ss->sp.image_size)
return SANE_STATUS_EOF;
do
{
if (ss->cancel)
/* ss->rpipe has already been closed by sane_cancel(). */
return SANE_STATUS_CANCELLED;
count = read (ss->rpipe, buf, size);
}
while (count == -1 && errno == EINTR);
if (count == -1)
{
if (errno == EAGAIN)
return SANE_STATUS_GOOD;
if (!ss->cancel)
{
PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n",
strerror (errno)));
}
close (ss->rpipe);
ss->rpipe = -1;
terminate_reader_task (ss, NULL);
return SANE_STATUS_IO_ERROR;
}
/* here count >= 0 */
ss->image_bytes_read += count;
if (ss->image_bytes_read > ss->sp.image_size)
{
PDBG (pixma_dbg (1, "BUG:ss->image_bytes_read > ss->sp.image_size\n"));
}
if (ss->image_bytes_read >= ss->sp.image_size)
{
close (ss->rpipe);
ss->rpipe = -1;
terminate_reader_task (ss, NULL);
}
else if (count == 0)
{
PDBG (pixma_dbg (3, "read_image():reader task closed the pipe:"
"%u bytes received, %u bytes expected\n",
ss->image_bytes_read, ss->sp.image_size));
close (ss->rpipe);
ss->rpipe = -1;
if (terminate_reader_task (ss, &status) != -1
&& status != SANE_STATUS_GOOD)
{
return status;
}
else
{
/* either terminate_reader_task failed or
rpipe was closed but we expect more data */
return SANE_STATUS_IO_ERROR;
}
}
if (readlen)
*readlen = count;
return SANE_STATUS_GOOD;
}
/*******************************************************************
** SANE API
*******************************************************************/
SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
int status, myversion, i;
SANEI_Config config;
UNUSED (authorize);
if (!version_code)
return SANE_STATUS_INVAL;
myversion = 100 * PIXMA_VERSION_MAJOR + PIXMA_VERSION_MINOR;
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, myversion);
DBG_INIT ();
sanei_thread_init ();
pixma_set_debug_level (DBG_LEVEL);
PDBG(pixma_dbg(2, "pixma is compiled %s pthread support.\n",
(sanei_thread_is_forked () ? "without" : "with")));
for (i = 0; i < MAX_CONF_DEVICES; i++)
conf_devices[i] = NULL;
config.count = 0;
config.descriptors = NULL;
config.values = NULL;
if (sanei_configure_attach(PIXMA_CONFIG_FILE, &config, config_attach_pixma) !=
SANE_STATUS_GOOD)
PDBG(pixma_dbg(2, "Could not read pixma configuration file: %s\n",
PIXMA_CONFIG_FILE));
status = pixma_init ();
if (status < 0)
{
PDBG (pixma_dbg (2, "pixma_init() failed %s\n", pixma_strerror (status)));
}
return map_error (status);
}
void
sane_exit (void)
{
while (first_scanner)
sane_close (first_scanner);
cleanup_device_list ();
pixma_cleanup ();
}
SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
UNUSED (local_only);
if (!device_list)
return SANE_STATUS_INVAL;
find_scanners ();
*device_list = dev_list;
return (dev_list) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
}
SANE_Status
sane_open (SANE_String_Const name, SANE_Handle * h)
{
unsigned i, nscanners;
int error = 0;
pixma_sane_t *ss = NULL;
const pixma_config_t *cfg;
if (!name || !h)
return SANE_STATUS_INVAL;
*h = NULL;
nscanners = pixma_find_scanners (conf_devices);
if (nscanners == 0)
return SANE_STATUS_INVAL;
if (name[0] == '\0')
name = pixma_get_device_id (0);
/* Have we already opened the scanner? */
for (ss = first_scanner; ss; ss = ss->next)
{
if (strcmp (pixma_get_string (ss->s, PIXMA_STRING_ID), name) == 0)
{
/* We have already opened it! */
return SANE_STATUS_DEVICE_BUSY;
}
}
i = 0;
while (strcmp (pixma_get_device_id (i), name) != 0)
{
if (++i >= nscanners)
return SANE_STATUS_INVAL;
}
cfg = pixma_get_device_config (i);
if ((cfg->cap & PIXMA_CAP_EXPERIMENT) != 0)
{
#ifndef NDEBUG
pixma_dbg (1, "WARNING:"
"Experimental backend CAN DAMAGE your hardware!\n");
if (getenv_atoi ("PIXMA_EXPERIMENT", 0) == 0)
{
pixma_dbg (1, "Experimental SANE backend for %s is disabled "
"by default.\n", pixma_get_device_model (i));
pixma_dbg (1, "To enable it, set the environment variable "
"PIXMA_EXPERIMENT to non-zero.\n");
return SANE_STATUS_UNSUPPORTED;
}
#else
return SANE_STATUS_UNSUPPORTED;
#endif
}
ss = (pixma_sane_t *) calloc (1, sizeof (*ss));
if (!ss)
return SANE_STATUS_NO_MEM;
ss->next = first_scanner;
first_scanner = ss;
ss->reader_taskid = -1;
ss->wpipe = -1;
ss->rpipe = -1;
ss->idle = SANE_TRUE;
ss->scanning = SANE_FALSE;
error = pixma_open (i, &ss->s);
if (error < 0)
{
sane_close (ss);
return map_error (error);
}
pixma_enable_background (ss->s, 0);
init_option_descriptors (ss);
*h = ss;
return SANE_STATUS_GOOD;
}
void
sane_close (SANE_Handle h)
{
pixma_sane_t **p, *ss;
for (p = &first_scanner; *p && *p != (pixma_sane_t *) h; p = &((*p)->next))
{
}
if (!(*p))
return;
ss = *p;
sane_cancel (ss);
pixma_close (ss->s);
*p = ss->next;
free (ss);
}
const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
{
DECL_CTX;
if (ss && 0 <= n && n < opt_last)
return &SOD (n);
return NULL;
}
SANE_Status
sane_control_option (SANE_Handle h, SANE_Int n,
SANE_Action a, void *v, SANE_Int * i)
{
DECL_CTX;
SANE_Int info = 0;
int error;
option_descriptor_t *opt;
if (i)
*i = 0;
if (!ss)
return SANE_STATUS_INVAL;
if (n < 0 || n >= opt_last)
return SANE_STATUS_UNSUPPORTED;
if (!ss->idle && a != SANE_ACTION_GET_VALUE)
{
PDBG (pixma_dbg (3, "Warning: !idle && !SANE_ACTION_GET_VALUE\n"));
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
return SANE_STATUS_INVAL;
}
opt = &(OPT_IN_CTX[n]);
if (!SANE_OPTION_IS_ACTIVE (opt->sod.cap))
return SANE_STATUS_INVAL;
switch (a)
{
case SANE_ACTION_SET_VALUE:
if ((opt->sod.type != SANE_TYPE_BUTTON && !v) ||
!SANE_OPTION_IS_SETTABLE (opt->sod.cap))
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
break;
case SANE_ACTION_SET_AUTO:
if (!(opt->sod.cap & SANE_CAP_AUTOMATIC) ||
!SANE_OPTION_IS_SETTABLE (opt->sod.cap))
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
break;
case SANE_ACTION_GET_VALUE:
if (!v || !(opt->sod.cap & SANE_CAP_SOFT_DETECT))
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
break;
default:
return SANE_STATUS_UNSUPPORTED;
}
error = control_option (ss, n, a, v, &info);
if (error == SANE_STATUS_GOOD && i)
*i = info;
return error;
}
SANE_Status
sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
{
DECL_CTX;
pixma_scan_param_t temp, *sp;
if (!ss || !p)
return SANE_STATUS_INVAL;
if (!ss->idle)
{
sp = &ss->sp; /* sp is calculated in sane_start() */
}
else
{
calc_scan_param (ss, &temp);
sp = &temp;
}
p->format = (sp->channels == 3) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
p->last_frame = SANE_TRUE;
p->lines = sp->h;
p->depth = sp->depth;
p->pixels_per_line = sp->w;
/* p->bytes_per_line = sp->line_size; NOTE: It should work this way, but it doesn't. No SANE frontend can cope with this. */
p->bytes_per_line = sp->w * sp->channels * (sp->depth / 8);
return SANE_STATUS_GOOD;
}
SANE_Status
sane_start (SANE_Handle h)
{
DECL_CTX;
int error = 0;
if (!ss)
return SANE_STATUS_INVAL;
if (!ss->idle && ss->scanning)
{
PDBG (pixma_dbg (3, "Warning in Sane_start: !idle && scanning. idle=%d, ss->scanning=%d\n",
ss->idle, ss->scanning));
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
return SANE_STATUS_INVAL;
}
ss->cancel = SANE_FALSE;
if (ss->idle ||
ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED ||
ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU)
ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */
else
ss->page_count++;
if (calc_scan_param (ss, &ss->sp) < 0)
return SANE_STATUS_INVAL;
ss->image_bytes_read = 0;
/* TODO: Check paper here in sane_start(). A function like
pixma_get_status() is needed. */
error = start_reader_task (ss);
if (error >= 0)
{
ss->output_line_size = ss->sp.w * ss->sp.channels * (ss->sp.depth / 8);
ss->byte_pos_in_line = 0;
ss->last_read_status = SANE_STATUS_GOOD;
ss->scanning = SANE_TRUE;
ss->idle = SANE_FALSE;
}
return map_error (error);
}
SANE_Status
sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
{
DECL_CTX;
int sum, n;
SANE_Byte temp[60];
SANE_Status status;
if (len)
*len = 0;
if (!ss || !buf || !len)
return SANE_STATUS_INVAL;
if (ss->cancel)
return SANE_STATUS_CANCELLED;
if ((ss->idle)
&& (ss->sp.source == PIXMA_SOURCE_ADF || ss->sp.source == PIXMA_SOURCE_ADFDUP))
return SANE_STATUS_INVAL;
if (!ss->scanning)
return ss->last_read_status;
status = SANE_STATUS_GOOD;
if ((ss->sp.line_size - ss->output_line_size) == 0)
{
status = read_image (ss, buf, maxlen, &sum);
}
else
{
/* FIXME: Because there is no frontend that can cope with padding at
the end of line, we've to remove it here in the backend! */
sum = 0;
while (sum < maxlen)
{
if (ss->byte_pos_in_line < ss->output_line_size)
{
n = ss->output_line_size - ss->byte_pos_in_line;
if ((maxlen - sum) < n)
n = maxlen - sum;
status = read_image (ss, buf, n, &n);
if (n == 0)
break;
sum += n;
buf += n;
ss->byte_pos_in_line += n;
}
else
{
/* skip padding */
n = ss->sp.line_size - ss->byte_pos_in_line;
if (n > (int) sizeof (temp))
{
PDBG (pixma_dbg (3, "Inefficient skip buffer. Should be %d\n", n));
n = sizeof (temp);
}
status = read_image (ss, temp, n, &n);
if (n == 0)
break;
ss->byte_pos_in_line += n;
if (ss->byte_pos_in_line == ss->sp.line_size)
ss->byte_pos_in_line = 0;
}
}
}
if (ss->cancel)
status = SANE_STATUS_CANCELLED;
else if ((status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF) &&
sum > 0)
{
*len = sum;
status = SANE_STATUS_GOOD;
}
ss->scanning = (status == SANE_STATUS_GOOD);
ss->last_read_status = status;
return status;
}
void
sane_cancel (SANE_Handle h)
{
DECL_CTX;
if (!ss)
return;
ss->cancel = SANE_TRUE;
if (ss->idle)
return;
close (ss->rpipe);
ss->rpipe = -1;
terminate_reader_task (ss, NULL);
ss->idle = SANE_TRUE;
}
SANE_Status
sane_set_io_mode (SANE_Handle h, SANE_Bool m)
{
DECL_CTX;
if (!ss || ss->idle || ss->rpipe == -1)
return SANE_STATUS_INVAL;
#ifdef HAVE_FCNTL_H
PDBG (pixma_dbg (2, "Setting %sblocking mode\n", (m) ? "non-" : ""));
if (fcntl (ss->rpipe, F_SETFL, (m) ? O_NONBLOCK : 0) == -1)
{
PDBG (pixma_dbg
(1, "WARNING:fcntl(F_SETFL) failed %s\n", strerror (errno)));
return SANE_STATUS_UNSUPPORTED;
}
return SANE_STATUS_GOOD;
#else
return (m) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
#endif
}
SANE_Status
sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
{
DECL_CTX;
*fd = -1;
if (!ss || !fd || ss->idle || ss->rpipe == -1)
return SANE_STATUS_INVAL;
*fd = ss->rpipe;
return SANE_STATUS_GOOD;
}
/*
BEGIN SANE_Option_Descriptor
rem -------------------------------------------
type group
title Scan mode
type int resolution
unit dpi
constraint @word_list = ss->dpi_list
default 75
title @SANE_TITLE_SCAN_RESOLUTION
desc @SANE_DESC_SCAN_RESOLUTION
cap soft_select soft_detect automatic
info reload_params
type string mode[10]
default Color
constraint @string_list = ss->mode_list
title @SANE_TITLE_SCAN_MODE
desc @SANE_DESC_SCAN_MODE
cap soft_select soft_detect automatic
info reload_params
type string source[30]
constraint @string_list = ss->source_list
title @SANE_TITLE_SCAN_SOURCE
desc @SANE_DESC_SCAN_SOURCE
default Flatbed
cap soft_select soft_detect
type bool button-controlled
title Button-controlled scan
desc 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.
default SANE_FALSE
cap soft_select soft_detect inactive
rem -------------------------------------------
type group
title Gamma
type bool custom-gamma
default SANE_TRUE
title @SANE_TITLE_CUSTOM_GAMMA
desc @SANE_DESC_CUSTOM_GAMMA
cap soft_select soft_detect automatic inactive
type int gamma-table[4096]
constraint (0,255,0)
title @SANE_TITLE_GAMMA_VECTOR
desc @SANE_DESC_GAMMA_VECTOR
cap soft_select soft_detect automatic inactive
rem -------------------------------------------
type group
title Geometry
type fixed tl-x
unit mm
default 0
constraint @range = &ss->xrange
title @SANE_TITLE_SCAN_TL_X
desc @SANE_DESC_SCAN_TL_X
cap soft_select soft_detect automatic
info reload_params
type fixed tl-y
unit mm
default 0
constraint @range = &ss->yrange
title @SANE_TITLE_SCAN_TL_Y
desc @SANE_DESC_SCAN_TL_Y
cap soft_select soft_detect automatic
info reload_params
type fixed br-x
unit mm
default _MAX
constraint @range = &ss->xrange
title @SANE_TITLE_SCAN_BR_X
desc @SANE_DESC_SCAN_BR_X
cap soft_select soft_detect automatic
info reload_params
type fixed br-y
unit mm
default _MAX
constraint @range = &ss->yrange
title @SANE_TITLE_SCAN_BR_Y
desc @SANE_DESC_SCAN_BR_Y
cap soft_select soft_detect automatic
info reload_params
rem -------------------------------------------
type group
title Buttons
type button button-update
title Update button state
cap soft_select soft_detect advanced
type int button-1
default 0
title Button 1
cap soft_select soft_detect advanced
type int button-2
default 0
title Button 2
cap soft_select soft_detect advanced
rem -------------------------------------------
END SANE_Option_Descriptor
*/
#include "pixma_sane_options.c" /* generated by gen_options.py */